Skip to content

Usage Cheat Sheet

Richard Keenan edited this page Feb 10, 2017 · 4 revisions

Introduction

Most of the following examples will use the Order class defined below to show how to use and customise JFixture.

public class Order {
   public String symbol;
   public int size;
   public BigDecimal price;
   public Iterable<String> previousOwners;
}

Objects with mutable, public fields obviously aren't recommended - but it keeps the example shorter here.

Object Creation

Creating an object at runtime is as simple as using

JFixture fixture = new JFixture();
Order order = fixture.create(Order.class);

This will ask the Fixture to create an instance of Order using the default rules.

@Fixture Annotations

In the vein of Mockito's @Mock annotation, JFixture offers the @Fixture annotation. This can be used in one of two two ways.

FixtureRule @Rule

The preferred approach is to use the FixtureRule class annotated with @Rule. All fields marked with the annotations will be initialised using a new JFixture instance, or a JFixture instance provided as a parameter. The Rule is run before any @Before methods so you can use fixtured fields in your @Befores to setup Mock behaviours for example. Note that the FixtureRule field must be public due to the way JUnit handles @Rule fields.

@Rule public FixtureRule fr = FixtureRule.initFixtures() // or FixtureRule.initFixtures(fixture);
@Fixture private Order order;

FixtureAnnotations

There shouldn't be any need to use this technique, the FixtureRule is the preferred method, but here's some info just in case. This behaves just like Mockito's MockitoAnnotations class. All fields marked with the annotations will be initialised using a new JFixture instance, or a JFixture instance provided as a parameter.

@Fixture private Order order;
 
@Before
public void setup() {
    FixtureAnnotations.initFixtures(this); // or initFixtures(this, fixture) where fixture has customisations applied
}

Constrained Requests

The usual usage of JFixture is to request an instance of a Class but there are other requests that the library supports. These can be thought of as extensions to the core functionality and can be called whether you're using the Fixture instance or @Fixture annotation.

Range

A ranged request contains a minimum value and a maximum value. JFixture will produce a value between those ranges. Only numeric ranges are currently supported.

Using @Fixture annotation

@Fixture // You still need to include this annotation
@Range(min = 1000L, max = 2000L)
private Long value;

Using JFixture instance

Long value = fixture.create().inRange(Long.class, 1000L, 2000L);

FromList

A "from list' request contains a List of objects for JFixture to choose from.

Using @Fixture annotation

Due to restrictions enforced by the compiler regarding the use of annotations this method isn't as flexible as the fluent method approach. You need to use either the 'strings' parameter and supply an array of strings, or the 'numbers' parameter and supply an array of doubles (the actual type your resolving doesn't need to be double.)

@Fixture // You still need to include this annotation
@FromListOf(strings = {"a", "b", "c"}) // Note the 'strings' parameter
private String letter;
 
@Fixture
@FromListOf(numbers = {1, 2, 3}) // Note the 'numbers' parameter
private Long number;

Customisation

JFixture is easily customisable to support requirements where the default object creation system isn't desirable.

Overriding all values of the same type

fixture.customise().sameInstance(String.class, "foo");
// Type must be provided due to complications with type erasure.

This will cause "symbol" and all elements inside "previousOwners" to be the string "foo".

Overriding values using a factory method

fixture.customise().lazyInstance(String.class, () -> String.valueOf(new Date().getTime()));

This will return the time when create() was called for each String request. Note the Java 8 syntax here for brevity.

Overriding type with subtypes

To supply a more specific subtype when resolving a class you use a subtype customisation.

fixture.customise().useSubType(Iterable.class, LinkedList.class);

In this example the previousOwners property will be a LinkedList rather than the default ArrayList. This is particularly useful for interfaces or abstract classes because JFixture probably won't know how to resolve those.

Fluent customisations

Customisations can be built up fluently

fixture.customise()
       .sameInstance(String.class, "foo")
       .useSubType(Iterable.class, LinkedList.class);

Overriding properties of types

To set a specific value to be used in object setters you use a property customisation.

fixture.customise().propertyOf(Order.class, "size", 123);

This looks for a setter called setSize on the Order type and uses the value 123 instead of the default. All instances of Order will use this value.

Repeat count

To override the number of items inserted into collection types you set the repeat count.

fixture.customise().repeatCount(5);

Handling circular dependencies

JFixture detects circular dependencies (e.g. ObjectA has property ObjectB, ObjectB has property ObjectA). By default this will throw an exception if you try to create such an object. This can be customised to ignore circular dependencies (e.g. ObjectA is created, ObjectB is created, but the next ObjectA will be null).

fixture.customise().circularDependencyBehaviour().omitSpecimen();

Handling unresolvable requests

If a request cannot be resolved (e.g. request for an interface type) the default behaviour is to throw an exception. If this isn't desired you can tell JFixture to omit the specimen instead which will leave the value as null;

fixture.customise().noResolutionBehaviour().omitSpecimen();

Tracing

To write output of the handling requests in order to debug unexpected behaviour you can add a tracing customisation to the Fixture instance. Tracing takes an instance of an Appendable interface (such as System.out, or StringBuilder.)

TracingCustomisation systemOutTracing = new TracingCustomisation(System.out);
fixture.customise(systemOutTracing);
fixture.create(Order.class);