Skip to main content
Version: v1.1.x

Overview

When you're starting with Fixture Monkey, using options can seem overwhelming. This guide helps you understand how to navigate the options and where to start.

Options vs ArbitraryBuilder API

There are two main ways to configure test data in Fixture Monkey:

  1. Options

    • Set during FixtureMonkey instance creation
    • Define global rules that apply to all test data generation
    • Reusable configurations
  2. ArbitraryBuilder API

    • Set during individual test data creation
    • One-time settings needed for specific test cases
    • More fine-grained control

Example:

// Using options - applies to all Product instances
FixtureMonkey fixtureMonkey = FixtureMonkey.builder()
.defaultNotNull(true) // Set all fields to non-null
.register(Product.class, fm -> fm.giveMeBuilder(Product.class)
.size("items", 3)) // items always has 3 elements
.build();

// Using ArbitraryBuilder API - applies only to this test
Product specificProduct = fixtureMonkey.giveMeBuilder(Product.class)
.set("name", "Test Product") // Set name just for this test
.set("price", 1000) // Set price just for this test
.sample();

Why Should You Use Options?

There are important reasons to use options:

1. Test Data Consistency

  • Problem: Need to apply the same rules across multiple tests
    // Without options - need to repeat settings in every test
    Product product1 = fixtureMonkey.giveMeBuilder(Product.class)
    .set("price", Arbitraries.longs().greaterThan(0))
    .sample();

    Product product2 = fixtureMonkey.giveMeBuilder(Product.class)
    .set("price", Arbitraries.longs().greaterThan(0))
    .sample();
    // With options - set once, apply everywhere
    FixtureMonkey fixtureMonkey = FixtureMonkey.builder()
    .register(Product.class, fm -> fm.giveMeBuilder(Product.class)
    .set("price", Arbitraries.longs().greaterThan(0)))
    .build();

    Product product1 = fixtureMonkey.giveMeOne(Product.class); // Automatically positive price
    Product product2 = fixtureMonkey.giveMeOne(Product.class); // Automatically positive price

2. Test Maintainability

  • Problem: Need to modify all tests when rules change
    // Using options - manage rules in one place
    public class TestConfig {
    public static FixtureMonkey createFixtureMonkey() {
    return FixtureMonkey.builder()
    .defaultNotNull(true)
    .register(Product.class, fm -> fm.giveMeBuilder(Product.class)
    .set("price", Arbitraries.longs().greaterThan(0))
    .set("stock", Arbitraries.integers().greaterThan(0)))
    .register(Order.class, fm -> fm.giveMeBuilder(Order.class)
    // Add orderRules() builder chaining here
    )
    .build();
    }
    }

3. Domain Rule Application

  • Problem: Need to apply business rules to test data
    // Applying domain rules through options
    FixtureMonkey fixtureMonkey = FixtureMonkey.builder()
    .register(Order.class, fm -> fm.giveMeBuilder(Order.class)
    .thenApply((order, b) -> {
    b.set("totalAmount", order.getItems().stream()
    .mapToInt(Item::getPrice)
    .sum()
    );
    })
    )
    .build();

Most Common Options for Beginners

Here are the essential options you'll likely need first:

1. defaultNotNull Option - Preventing null Values

The defaultNotNull option ensures that properties not explicitly marked as nullable (with annotations like @Nullable in Java or ? in Kotlin) will not be null. This is useful when you want to avoid null-related issues in your tests.

note

The Java examples below also set objectIntrospector(ConstructorPropertiesArbitraryIntrospector.INSTANCE) because the sample Product class uses Lombok @Value (which generates an all-args constructor with @ConstructorProperties). Choose the introspector that matches your class type.

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

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

// then
then(product.getProductName()).isNotNull();
then(product.getPrice()).isNotNull();
then(product.getCategory()).isNotNull();

2. javaTypeArbitraryGenerator Option - Controlling Basic Type Generation

The javaTypeArbitraryGenerator option allows you to customize how basic Java types (String, Integer, etc.) are generated. This option is applied through the JqwikPlugin, which integrates Fixture Monkey with the Jqwik property-based testing library.

JavatestJavaTypeArbitraryGeneratorOption()
OverviewTest.java
// given
FixtureMonkey fixtureMonkey = FixtureMonkey.builder()
.plugin(
new JqwikPlugin()
.javaTypeArbitraryGenerator(new JavaTypeArbitraryGenerator() {
@Override
public StringArbitrary strings() {
return Arbitraries.strings().alpha().ofLength(10);
}
})
)
.build();

// when
String generatedString = fixtureMonkey.giveMeOne(String.class);

// then
then(generatedString).hasSize(10);
then(generatedString).matches("[a-zA-Z]+");

3. register Option - Setting Type-Specific Default Rules

The register option allows you to configure default settings for specific types. This is useful when you have consistent requirements for a class across multiple tests.

JavatestRegisterOption()
OverviewTest.java
// given
FixtureMonkey fixtureMonkey = FixtureMonkey.builder()
.objectIntrospector(ConstructorPropertiesArbitraryIntrospector.INSTANCE)
.register(
Product.class,
fm -> fm.giveMeBuilder(Product.class)
.set("price", Arbitraries.longs().greaterOrEqual(1))
.set("category", "Electronics")
)
.build();

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

// then
then(product.getPrice()).isPositive();
then(product.getCategory()).isEqualTo("Electronics");

4. plugin Option - Adding Extended Functionality

The plugin option allows you to integrate additional features provided by various plugins that Fixture Monkey offers. This option is essential when working with specific frameworks or libraries.

@Test
void testPluginOption() {
// Create a FixtureMonkey with Jackson plugin for JSON support
FixtureMonkey fixtureMonkey = FixtureMonkey.builder()
.plugin(new JacksonPlugin()) // Add support for Jackson annotations
.build();

// Now you can create objects with proper Jackson annotation support
JsonProduct product = fixtureMonkey.giveMeOne(JsonProduct.class);

// Jackson annotations on JsonProduct will be respected
// (e.g., @JsonProperty, @JsonIgnore, etc.)
}

5. defaultArbitraryContainerInfoGenerator Option - Controlling Container Sizes

The defaultArbitraryContainerInfoGenerator option allows you to control the size of generated container types such as lists, sets, and maps. This option is useful when you need containers of specific sizes in your tests.

JavatestContainerSizeOption()
OverviewTest.java
// given
FixtureMonkey fixtureMonkey = FixtureMonkey.builder()
.defaultArbitraryContainerInfoGenerator(context -> new ArbitraryContainerInfo(3, 3))
.build();

// when
List<String> stringList = fixtureMonkey.giveMeOne(new TypeReference<List<String>>() {});
Map<Integer, String> map = fixtureMonkey.giveMeOne(new TypeReference<Map<Integer, String>>() {});

// then
then(stringList).hasSize(3);
then(map).hasSize(3);

Which Option to Use When?

Here's a simple guide to help you choose which option to use:

OptionWhen to Use
defaultNotNull(true)When you want to ensure test objects have no null values (except explicitly nullable properties)
javaTypeArbitraryGeneratorWhen you need to customize how basic types like strings or numbers are generated
register(Class, function)When you need consistent default values or constraints for a specific class
plugin(Plugin)When you need additional features like support for frameworks (Jackson, Kotlin, etc.)
defaultArbitraryContainerInfoGeneratorWhen you need to control the size of generated containers (lists, sets, maps, etc.)

Understanding Option Scope

There are important points to understand when using options:

  1. Instance Scope
    • Options only apply to the FixtureMonkey instance they're configured on
    • You can create multiple instances with different settings
// Test settings
FixtureMonkey testFixture = FixtureMonkey.builder()
.defaultNotNull(true)
.build();

// Development settings
FixtureMonkey devFixture = FixtureMonkey.builder()
.defaultNotNull(false)
.build();
  1. Option Priority
    • More specific options take precedence over general ones
    • Later options override earlier ones
FixtureMonkey fixture = FixtureMonkey.builder()
.defaultNotNull(true) // All fields non-null
.register(Product.class, fm -> fm.giveMeBuilder(Product.class)
.setNull("description")) // Allow null for description only
.build();

Next Steps

We recommend following this learning path to make the most of Fixture Monkey:

  1. Start here: Overview (this page) - Understand what options are and how they're structured
  2. Next step for beginners: Essential Options for Beginners - Learn the most commonly used options for everyday testing needs
  3. Understand the concepts: Option Concepts - Gain deeper knowledge of how options work internally and learn key terminology
  4. Advanced features: Advanced Options for Experts - Explore options for complex testing scenarios