Skip to main content
Version: v1.1.x

Introspector

What is an Introspector?

An Introspector in Fixture Monkey is simply a tool that determines how test objects are created. Think of it as a "factory" that figures out the best way to create objects for your tests.

For example, it decides:

  • Whether to use a constructor or a builder to create objects
  • How to set values for fields
  • How to handle different types of classes in your codebase

If you're new to Fixture Monkey and want to get started quickly, here's the setup that works for most projects:

JavarecommendedSetup()
IntrospectorTest.java
// given
FixtureMonkey fixtureMonkey = FixtureMonkey.builder()
.objectIntrospector(new FailoverIntrospector(
Arrays.asList(
ConstructorPropertiesArbitraryIntrospector.INSTANCE,
BuilderArbitraryIntrospector.INSTANCE,
FieldReflectionArbitraryIntrospector.INSTANCE,
BeanArbitraryIntrospector.INSTANCE
),
false
))
.build();

// when
Customer customer = fixtureMonkey.giveMeOne(Customer.class);

// then
then(customer).isNotNull();

This setup combines multiple strategies to handle different class types, so it works well for most real-world projects without additional configuration.

Simplest Approach (If You Just Want Basic Setup)

If you prefer the simplest possible setup, you can use the default configuration:

JavasimplestApproach()
IntrospectorTest.java
// given
FixtureMonkey fixtureMonkey = FixtureMonkey.builder()
.build();

// when
Customer customer = fixtureMonkey.giveMeOne(Customer.class);

// then
then(customer).isNotNull();

However, this basic approach only works well with simple JavaBean classes that have a no-arguments constructor and setter methods.

Choosing the Right Introspector for Your Classes

Different class types require different approaches to object creation. Here's a simple guide to help you choose:

Class TypeRecommended IntrospectorExample
Classes with setters (JavaBeans)BeanArbitraryIntrospectorClasses with getters/setters
Immutable classes with constructorsConstructorPropertiesArbitraryIntrospectorRecords, classes with annotated constructors
Classes with mixed field accessFieldReflectionArbitraryIntrospectorClasses with public fields, no-args constructor
Classes using builder patternBuilderArbitraryIntrospectorClasses with .builder() method
Mixed codebase with different patternsFailoverArbitraryIntrospectorProjects with various class types

Examples for Common Class Types

note

The examples below use .defaultNotNull(true) to ensure generated properties are non-null for assertion purposes. This is optional — remove it if null values are acceptable in your tests.

Example 1: Standard JavaBean Class (with getters/setters)

JavatestCustomer()
IntrospectorTest.java
// given
FixtureMonkey fixtureMonkey = FixtureMonkey.builder()
.objectIntrospector(BeanArbitraryIntrospector.INSTANCE)
.defaultNotNull(true)
.build();

// when
Customer customer = fixtureMonkey.giveMeOne(Customer.class);

// then
then(customer.getName()).isNotNull();

Example 2: Immutable Class with Constructor

JavatestProduct()
IntrospectorTest.java
// given
FixtureMonkey fixtureMonkey = FixtureMonkey.builder()
.objectIntrospector(ConstructorPropertiesArbitraryIntrospector.INSTANCE)
.defaultNotNull(true)
.build();

// when
Product product = fixtureMonkey.giveMeOne(Product.class);

// then
then(product.getName()).isNotNull();

Example 3: Class with Builder Pattern

JavatestUser()
IntrospectorTest.java
// given
FixtureMonkey fixtureMonkey = FixtureMonkey.builder()
.objectIntrospector(BuilderArbitraryIntrospector.INSTANCE)
.defaultNotNull(true)
.build();

// when
User user = fixtureMonkey.giveMeOne(User.class);

// then
then(user.getUsername()).isNotNull();
then(user.getEmail()).isNotNull();

Why Introspectors Matter

Different projects use different patterns for object creation:

  • Some use simple classes with getters/setters
  • Others use immutable objects with constructors
  • Some follow the builder pattern
  • Frameworks like Lombok generate code in specific ways

By choosing the right introspector, you can make Fixture Monkey work with your existing code without modifications, saving you time and effort.

Frequently Asked Questions (FAQ)

Q: I'm not sure which introspector to use. What should I do?

A: Start with the recommended setup (using FailoverIntrospector with multiple introspectors). It works for most projects and automatically tries different strategies.

JavafailoverIntrospector()
IntrospectorTest.java
FixtureMonkey fixtureMonkey = FixtureMonkey.builder()
.objectIntrospector(new FailoverIntrospector(
Arrays.asList(
ConstructorPropertiesArbitraryIntrospector.INSTANCE,
BuilderArbitraryIntrospector.INSTANCE,
FieldReflectionArbitraryIntrospector.INSTANCE,
BeanArbitraryIntrospector.INSTANCE
),
false
))
.build();

then(fixtureMonkey.giveMeOne(Customer.class)).isNotNull();

Q: My objects aren't being generated. What should I check?

A: Ensure your class has one of the following:

  • A no-args constructor with setters (for BeanArbitraryIntrospector)
  • A constructor with @ConstructorProperties (for ConstructorPropertiesArbitraryIntrospector)
  • A builder method (for BuilderArbitraryIntrospector)

Q: I'm using Lombok and my objects aren't generating properly. What should I do?

A: Add lombok.anyConstructor.addConstructorProperties=true to your lombok.config file and use ConstructorPropertiesArbitraryIntrospector.

Q: What if I need custom creation logic for a specific class?

A: For specific cases, you can use the instantiate method to specify how an instance should be created:

MySpecialClass object = fixtureMonkey.giveMeBuilder(MySpecialClass.class)
.instantiate(() -> new MySpecialClass(specialParam1, specialParam2))
.sample();

For more advanced custom logic, see the Custom Introspector guide, but most users won't need this.

Available Introspectors (More Details)

BeanArbitraryIntrospector (Default)

Best for: Standard JavaBean classes with setters

Requirements:

  • Class must have a no-args constructor
  • Class must have setter methods for properties
FixtureMonkey fixtureMonkey = FixtureMonkey.builder()
.objectIntrospector(BeanArbitraryIntrospector.INSTANCE) // This is the default
.build();

ConstructorPropertiesArbitraryIntrospector

Best for: Immutable objects with constructors

Requirements:

  • Class must have a constructor with @ConstructorProperties or be a record type
  • For Lombok, add lombok.anyConstructor.addConstructorProperties=true to lombok.config
FixtureMonkey fixtureMonkey = FixtureMonkey.builder()
.objectIntrospector(ConstructorPropertiesArbitraryIntrospector.INSTANCE)
.build();

FieldReflectionArbitraryIntrospector

Best for: Classes with field access

Requirements:

  • Class must have a no-args constructor
  • Fields can be accessed via reflection
FixtureMonkey fixtureMonkey = FixtureMonkey.builder()
.objectIntrospector(FieldReflectionArbitraryIntrospector.INSTANCE)
.build();

BuilderArbitraryIntrospector

Best for: Classes using the builder pattern

Requirements:

  • Class must have a builder with set methods and a build method
FixtureMonkey fixtureMonkey = FixtureMonkey.builder()
.objectIntrospector(BuilderArbitraryIntrospector.INSTANCE)
.build();

Best for: Projects with a mix of class types

Benefits:

  • Tries multiple introspectors in sequence
  • Works with various class patterns
  • Most versatile option
FixtureMonkey fixtureMonkey = FixtureMonkey.builder()
.objectIntrospector(new FailoverIntrospector(
Arrays.asList(
ConstructorPropertiesArbitraryIntrospector.INSTANCE,
BuilderArbitraryIntrospector.INSTANCE,
FieldReflectionArbitraryIntrospector.INSTANCE,
BeanArbitraryIntrospector.INSTANCE
),
false // Disable logging for cleaner test output
))
.build();

If you want to disable the fail log, set the constructor argument enableLoggingFail to false as shown above.

warning

Performance note: FailoverArbitraryIntrospector may increase generation costs as it attempts to create objects using each registered introspector in sequence. When performance is a concern, use a specific introspector if you know your class patterns.

PriorityConstructorArbitraryIntrospector

Best for: Special cases where other introspectors don't work

Benefits:

  • Uses available constructors even without @ConstructorProperties
  • Helpful for library classes you can't modify
FixtureMonkey fixtureMonkey = FixtureMonkey.builder()
.objectIntrospector(PriorityConstructorArbitraryIntrospector.INSTANCE)
.build();

Additional Introspectors from Plugins

Plugins provide additional introspectors for specific needs:

How Introspectors Work (Technical Details)

Need More Advanced Customization?

If you have special requirements for object creation that aren't covered by the built-in introspectors, you might need to create a custom introspector.

This is an advanced topic and most users won't need it. If you're interested, see the Custom Introspector guide.