Skip to content

Latest commit

 

History

History
481 lines (371 loc) · 13.6 KB

configuration-options.adoc

File metadata and controls

481 lines (371 loc) · 13.6 KB

Configuration options

All the configuration options available in KotlinFixture can be set when creating the fixture object as well as when creating a particular implementation.

You can also create a new fixture based on an existing one, which allows changes to the configuration.

val baseFixture = kotlinFixture {
    factory<Int> { 3 } // (1)
}

val fixture = baseFixture.new {
    factory<Long> { 100L } // (2)
}

println(fixture<Long>()) // Prints 100
println(fixture<Int>()) // Prints 3
println(fixture<Int> {
    factory<Int> { 4 } // (3)
}) // Prints 4
  1. Configuration when creating the fixture object.

  2. Create a new fixture based on an existing one, allowing overriding configuration.

  3. Configuration can also be overridden when creating a particular object.

Setting list and map length with repeatCount

Used to determine the length used for lists and maps. By default, the library generates 5 items.

val fixture = kotlinFixture {
    repeatCount { 3 }
}

val listOfThreeItems = fixture<List<Int>>() // 10, 81, 3

repeatCount is a factory method so can be used to return lists and maps of different lengths each execution:

repeatCount {
    random.nextInt(1, 5)
}

Additionally we can override repeatCount for properties of a class and nested lists and maps.

Kotlin class example

Given the following Kotlin class:

class KotlinClass(val readOnly: List<String>, private var private: List<String>) {
    var member: List<String>? = null
}

We can override repeatCount for KotlinClass as follows:

val fixture = kotlinFixture {
    // Public constructor parameters overridden by reference:
    repeatCount(KotlinClass::readOnly) { 1 }

    // Private constructor parameters are overridden by name:
    repeatCount<KotlinClass>("private") { 2 }

    // Public member properties overridden by reference:
    repeatCount(KotlinClass::member) { 3 }
}
Java class example

Given the following Java class:

public class JavaClass {
    private final List<String> constructor;
    private List<String> mutable;

    public JavaClass(List<String> constructor) { this.constructor = constructor; }

    public void setMutable(List<String> mutable) { this.mutable = mutable; }
}

We can override repeatCount for JavaClass as follows:

val fixture = kotlinFixture {
    // Setter overridden by reference:
    repeatCount(JavaClass::setMutable) { 1 }

    // Constructor parameters don't typically retain names and so are
    // overridden by a positional 'arg' names:
    repeatCount<JavaClass>("arg0") { 2 }
}

Resolving abstract superclasses to a chosen subclass with subType

Used to always return an instance of a particular subclass for a superclass.

val fixture = kotlinFixture {
    subType<Number, Int>()
}

val alwaysInt = fixture<Number>()

Customising class generation with factory

Used to return the given instance for a particular class using a factory method.

val fixture = kotlinFixture {
    factory<Number> {
        41
    }
}

val alwaysFortyOne = fixture<Number>()

As factory is a factory method you can return different values on every execution:

factory<Number> {
    random.nextInt(10, 50)
}

Generating values in a range

factory has a built-in range function to make it easy to generate values in a range.

val fixture = kotlinFixture {
    // Generate using ranges (and iterables)
    factory<Int> { range(1..10) }
}

val betweenOneAndTen = fixture<Int>()

Generating Date and Calendar values

By default, Date and Calendar instances pick a date within 10 years of 1 Jan 2020.

This can be overridden using the built-in constructs between, before and after in your factory definition:

val fixture = kotlinFixture {
    // Generate between two dates
    factory<Date> { between(startDate, endDate) }
}

val betweenTwoDates = fixture<Date>()

Customising generation of class properties with property

Used to override constructor parameters or mutable properties when generating instances of generic classes.

Kotlin class example

Given the following Kotlin class:

class KotlinClass(val readOnly: String, private var private: String) {
    var member: String? = null
}

We can override creating an instance of KotlinClass as follows:

val fixture = kotlinFixture {
    // Public constructor parameters overridden by reference:
    property(KotlinClass::readOnly) { "a" }

    // Private constructor parameters are overridden by name:
    property<KotlinClass, String>("private") { "b" }

    // Public member properties overridden by reference:
    property(KotlinClass::member) { "c" }
}
Java class example

Given the following Java class:

public class JavaClass {
    private final String constructor;
    private String mutable;

    public JavaClass(String constructor) { this.constructor = constructor; }

    public void setMutable(String mutable) { this.mutable = mutable; }
}

We can override creating an instance of JavaClass as follows:

val fixture = kotlinFixture {
    // Setter overridden by reference:
    property<String>(JavaClass::setMutable) { "d" }

    // Constructor parameters don't typically retain names and so are
    // overridden by a positional 'arg' names:
    property<JavaClass, String>("arg0") { "e" }
}

Filtering generated values with filter

Used to allow generated values to be filtered using standard sequence functions.

val fixture = kotlinFixture {
    filter<Int> {
        filter { it % 2 == 0 }
    }

    // Can be used to return distinct values.
    filter<String> {
        distinct()
    }
}

val evenNumber = fixture<Int>()

val evenNumberLessThan100 = fixture<Int> {
    // Builds upon the parent configuration
    filter<Int> {
        filter { it < 100 }
    }
}
⚠️

The sequence can hang indefinitely if the applied operators prevent the generation of new values. For example:

  • distinct will hang if we exhaust all available values. A good practice is to add a take(count) which will throw a NoSuchElementException if we try to generate more values.

  • filter that can never be fulfilled e.g. filter { false }

Providing a seeded random

By default, we generate unique values between runs using a default Random class. If you want repeatability you can specify a seeded Random instance.

val fixture = kotlinFixture {
    random = Random(seed = 10)
}

val alwaysTheSame = fixture<Int>()
ℹ️
While you can specify random at object creation, this will make the result static i.e. fixture<Int> { random = Random(seed = 5) } will always return the same value.

Overriding nullability with nullabilityStrategy

By default, when the library comes across a nullable type, such as String? it will randomly return a value or null. This can be overridden by setting a nullability strategy.

val fixture = kotlinFixture {
    // All nullable types will be populated with a value
    nullabilityStrategy(NeverNullStrategy)
}
Available strategies
NeverNullStrategy

populate nullable types with a non-null value.

AlwaysNullStrategy

populate nullable types with null.

RandomlyNullStrategy

populate nullable types randomly with null.

It is also possible to define and implement your own nullability strategy by implementing NullabilityStrategy and applying it as above.

Overriding the use of default values with optionalStrategy

By default, when the library comes across an optional type, such as value: String = "default" it will randomly return the default value, or a generated value. This can be overridden by setting an optional strategy.

val fixture = kotlinFixture {
    // All optionals will be populated with their default value
    optionalStrategy(AlwaysOptionalStrategy) {
        // You can override the strategy for a particular class
        classOverride<AnotherObject>(NeverOptionalStrategy)

        // You can override the strategy for a property of a class
        propertyOverride(AnotherObject::property, RandomlyOptionalStrategy)
    }
}
Available strategies
AlwaysOptionalStrategy

always use the properties default value.

NeverOptionalStrategy

never use the properties default value.

RandomlyOptionalStrategy

randomly use the properties default value.

It is also possible to define and implement your own optional strategy by implementing OptionalStrategy and applying it as above.

Changing how recursion behaves with recursionStrategy

When the library detects recursion, by default, it will throw an UnsupportedOperationException with the details of the circular reference. This strategy can be changed to instead return null for the reference, however, if this results in an invalid object an exception will still be thrown as the object requested couldn’t be resolved.

val fixture = kotlinFixture {
    recursionStrategy(NullRecursionStrategy)
}
Available strategies
NullRecursionStrategy

use null for circular references.

ThrowingRecursionStrategy

throw an exception when finding circular references.

UnresolvedRecursionStrategy

use Unresolved for circular references, which may result in generation of a valid object as other scenarios will be tried

It is also possible to define and implement your own recursion strategy by implementing RecursionStrategy and applying it as above.

Logging object generation with loggingStrategy

A basic logger can be applied using the built-in SysOutLoggingStrategy. It is also possible to define and implement your own logging strategy by implementing LoggingStrategy and applying it as below.

val fixture = kotlinFixture {
    loggingStrategy(SysOutLoggingStrategy)
}

The logger for fixture<String>() outputs:

ktype kotlin.String →
    class kotlin.String →
        Success(5878ec34-c30f-40c7-ad52-c15a39b44ac1)
    Success(5878ec34-c30f-40c7-ad52-c15a39b44ac1)

Choosing the constructor to generate an object with constructorStrategy

By default, when the library generates an instance of a class it picks a constructor at random. This can be overridden by setting a constructor strategy.

val fixture = kotlinFixture {
    constructorStrategy(ModestConstructorStrategy)
}
Available strategies
RandomConstructorStrategy

order constructors at random.

ModestConstructorStrategy

order constructors by the most modest constructor first. i.e. fewer parameters returned first.

GreedyConstructorStrategy

order constructors by the most greedy constructor first. i.e. greater parameters returned first.

ArrayFavouringConstructorStrategy

order constructors selecting those with the most parameters of Array<*> before any other.

ListFavouringConstructorStrategy

order constructors selecting those with the most parameters of List<*> before any other.

It is also possible to define and implement your own constructor strategy by implementing ConstructorStrategy and applying it as above.