Introspector
While you can change the way an object is created in the ArbitraryBuilder with instantiate, there may be cases where you want to change the way objects are created globally.
Fixture Monkey lets you choose the way you want to create your object by providing different Introspectors.
An Introspector defines the default way of how Fixture Monkey creates objects.
Each introspector has some kind of restrictions that the class must have in order for the introspector to generate instances of that class.
You can change the introspector you use by using the objectIntrospector option of Fixture Monkey.
BeanArbitraryIntrospector
The BeanArbitraryIntrospector is the default introspector that fixture monkey uses for object creation.
It creates new instances using reflection and the setter method, so the class it creates must have a no-args constructor and setters.
FixtureMonkey fixtureMonkey = FixtureMonkey.builder()
.objectIntrospector(BeanArbitraryIntrospector.INSTANCE)
.build();
ConstructorPropertiesArbitraryIntrospector
To generate an object with its given constructor, you can use ConstructorPropertiesArbitraryIntrospector.
For ConstructorPropertiesArbitraryIntrospector, the generated class should have a constructor with @ConstructorProperties or the class should be a record type.
(Or, if you are using Lombok, you can add lombok.anyConstructor.addConstructorProperties=true to the lombok.config file.)
When you create a record class and have multiple constructors, the constructor with the @ConstructorProperties annotation has priority.
FixtureMonkey fixtureMonkey = FixtureMonkey.builder()
.objectIntrospector(ConstructorPropertiesArbitraryIntrospector.INSTANCE)
.build();
FieldReflectionArbitraryIntrospector
FieldReflectionArbitraryIntrospector creates new instances with reflection and also sets the fields with reflection.
So the class to be generated must have a no-args constructor and one of the getters or setters.
FixtureMonkey fixtureMonkey = FixtureMonkey.builder()
.objectIntrospector(FieldReflectionArbitraryIntrospector.INSTANCE)
.build();
BuilderArbitraryIntrospector
To generate a class using the class’s builder, you can use BuilderArbitraryIntrospector.
It requires that the class has a builder.
FixtureMonkey fixtureMonkey = FixtureMonkey.builder()
.objectIntrospector(BuilderArbitraryIntrospector.INSTANCE)
.build();
FailoverArbitraryIntrospector
Sometimes your production code may contain several classes with different configurations, making it difficult to generate all objects with a single introspector.
In this case, you can use the FailoverArbitraryIntrospector.
This introspector allows you to use multiple introspectors, and it will continue the introspection even if one of the introspectors fails to generate.
FixtureMonkey sut = FixtureMonkey.builder()
.objectIntrospector(new FailoverIntrospector(
Arrays.asList(
FieldReflectionArbitraryIntrospector.INSTANCE,
ConstructorPropertiesArbitraryIntrospector.INSTANCE
)
))
.build();
If you want to disable the fail log, you should set the constructor argument enableLoggingFail to false.
FailoverIntrospector failoverIntrospector = new FailoverIntrospector(introspectors, false);
PriorityConstructorArbitraryIntrospector
Types that Fixture Monkey does not support creating by default can be created using a custom ArbitraryIntrospector.
However, creating your own ArbitraryIntrospector can be difficult if you are not familiar with Fixture Monkey.
To solve this difficulty, we provide a PriorityConstructorArbitraryIntrospector that uses a constructor to create the type.
Timestamp actual = FixtureMonkey.builder()
.objectIntrospector(PriorityConstructorArbitraryIntrospector.INSTANCE)
.build()
.giveMeOne(Timestamp.class);
Differences from ConstructorPropertiesArbitraryIntrospector
The ConstructorPropertiesArbitraryIntrospector is also an ArbitraryIntrospector that uses a constructor to create an object.
The differences from PriorityConstructorArbitraryIntrospector are as follows.
| PriorityConstructorArbitraryIntrospector | ConstructorPropertiesArbitraryIntrospector | |
|---|---|---|
Need @ConstructorProperties | No | Yes |
| Can customize the parameters of constructor | Optional (need withParameterNamesResolver ) | Yes |
| Criteria for choosing a constructor | By the constructorFilter, sortingCriteria | The first constructor with @ConstructorProperties |
constructorFilter
The PriorityConstructorArbitraryIntrospector uses the constructorFilter condition to determine which constructor to use for generation.
The constructorFilter can be changed using withConstructorFilter.
By default, it is constructor -> !Modifier.isPrivate(constructor.getModifiers()).
sortingCriteria
If there are multiple constructors that satisfy the constructorFilter condition,
an additional sortingCriteria condition is used to determine the constructor.
Use the first constructor when sorted by Comparator<Constructor<?>>.
The sortingCriteria can be changed using withSortingCriteria.
The default setting is the constructor with the least number of constructors.
Comparator.comparing(Constructor::getParameterCount)
parameterNamesResolver
Fixture Monkey cannot recognise constructor parameter names if any of the following three conditions are not met.
- record type
- Enable JVM option
-parameters - Existence of
@ConstructorPropertiesin the constructor
If you do not recognise constructor parameter names, you cannot use the ArbitraryBuilder API to control constructor parameters.
The PriorityConstructorArbitraryIntrospector uses the parameterNamesResolver to recognise parameter names.
The parameterNamesResolver can be changed using withParameterNamesResolver.
The entered parameter names must always be the same as the parameter order.
Additional introspectors have been introduced inside plugins, such as JacksonObjectArbitraryIntrospector or
PrimaryConstructorArbitraryIntrospector