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
-
Configuration when creating the fixture object.
-
Create a new fixture based on an existing one, allowing overriding configuration.
-
Configuration can also be overridden when creating a particular object.
- Setting list and map length with
repeatCount
- Resolving abstract superclasses to a chosen subclass with
subType
- Customising class generation with
factory
- Customising generation of class properties with
property
- Filtering generated values with
filter
- Providing a seeded
random
- Overriding nullability with
nullabilityStrategy
- Overriding the use of default values with
optionalStrategy
- Changing how recursion behaves with
recursionStrategy
- Logging object generation with
loggingStrategy
- Choosing the constructor to generate an object with
constructorStrategy
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 }
}
Used to always return an instance of a particular subclass for a superclass.
val fixture = kotlinFixture {
subType<Number, Int>()
}
val alwaysInt = fixture<Number>()
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)
}
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>()
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>()
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" }
}
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:
|
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.
|
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.
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.
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.
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)
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.