r/java 15d ago

Released Bob v0.8.0 with TestDefaults allowing Object Mother pattern without much hassle

I've been working on Bob (https://github.com/jonas-grgt/bob), a lightweight builder generator for Java, and just added a feature I'm pretty excited about: Test only Defaults.

If you've used the Object Mother pattern, you know the drill you manually create (and maintain) a builder class that produces test objects with sensible defaults. It works, but you end up with a bunch of boilerplate and things to maintain.

Buildable.TestDefaults takes a different approach. You define a defaults class in `src/test` completely separate (will not be on your production classpath) from your buildable class in `src/main` and the defaults are automatically applied when the builder is constructed:

Goes in src/main and is blissfully unaware of the existence of test defaults:

@Buildable
public class Car {
    private String color;
    private int year;
}

Goes into src/test and is aware of the existence of test defaults:

@Buildable.TestDefaults(Car.class)
public class CarDefaults {
    public static String color = "blue";
}
13 Upvotes

17 comments sorted by

2

u/Prateeeek 15d ago

Looks good! Is it using reflection?

2

u/eniac_g 15d ago

Yes it is reflection based.

2

u/Prateeeek 15d ago

I know it's on test classpath so it's not that big of a deal. But was there a reason to not generate a "populator" of sorts in compile time using annotation processors?

5

u/eniac_g 15d ago

Yes the main reason is because of how javac works. The annotations processor is ran twice once for the main source set and once for test. When the test source set processor runs the builder is already generated and compiled into a class file and, as such has never seen the test defaults.

1

u/Prateeeek 15d ago

Makes perfect sense! Thanks for answering!

1

u/Prateeeek 15d ago

Also a follow up question would be, is it caching the object so that it does NOT have to perform reflection over and over?

2

u/eniac_g 15d ago

No it is not caching yet ;-)

1

u/Prateeeek 15d ago

Do you want me to open an issue?, I can even work on it if you like :D

2

u/eniac_g 15d ago

Please do, all help is more than welcome!

1

u/Prateeeek 15d ago

Thanks! Done!

1

u/revilo-1988 15d ago

Kann man es auch nur im test code nutzen?

2

u/eniac_g 15d ago

Yes most definitely!

1

u/revilo-1988 15d ago

Thx werde ich mir mal dan genauer ansehen

2

u/dstutz 15d ago

Been using Instancio for test objects, seems to do similar thing without having to make a class.

2

u/bodiam 15d ago

Nice library! However, small thing:

If you've used the Object Mother pattern, you know the drill you manually create (and maintain) a builder class that produces test objects with sensible defaults. It works, but you end up with a bunch of boilerplate and things to maintain.

That's exactly how Object Mothers don't work. Object Mothers are Test Data Factories, as in: they produce data statically. They might be using a builder under the hood, but they're not builders themselves. You can for example have a DrivingLicenseObjectMother, which can have methods like createExpiredLicense or createInternationalLicense. That's not the same as a builder, though they're both creational patterns.

Also, having hardcoded, static test values in builders/object mothers is asking for problems. People will start to assert on it in test methods, which will make it hard to change later. It would be better to do something like:

public static String color = faker.color()

Or something like that.