EasyRandom 4.0, the library to easily create test data, has been released. In this blogpost, I’ll walk you through some of the new features, most notably the three strategy classes to easily change the internal behaviour of the randomisation process.
What’s new in EasyRandom 4.0
With the EasyRandom 4.0 release, several things have been changed. One of the big changes is the new EasyRandom name. Formerly known as RandomBeans, the project has been renamed and the project is now known as EasyRandom. Furthermore, EasyRandom is now part of the Jeasy family. Jeasy is a collection of easy to use Java libraries, much like Jodd, Guava, and Apache Commons.
Due to this rename, several classes and all of the packages have been renamed. The new classes now live in the org.jeasy.random
package, so if you have been using RandomBeans before, a small migration might be needed. For details on how to migrate can be found in the migration guide.
Another change is the way how to customize test data. There is now more control over the test data generation with the introduction of ExclusionPolicy
, ObjectFactory
and RandomizerProvider
.
Creating test data
After adding the EasyRandom 4.0 dependency to your build configuration like this:
testImplementation group: 'org.jeasy', name: 'easy-random-core', version: '4.0.0'
You can create random test data using the following snippet:
import org.jeasy.random.EasyRandom
data class Product(val name: String, val price: BigDecimal)
val easyRandom = EasyRandom()
val product = easyRandom.nextObject(Product::class.java)
In the above example, a new instance of the Product class has been created with random values. In the case of reproducible random values, a seed can be used so that on every generation the random values are the same:
import org.jeasy.random.EasyRandomParameters
val parameters = EasyRandomParameters().seed(123L)
val easyRandom = EasyRandom(parameters)
val product = easyRandom.nextObject(Product::class.java)
Customizing test data generation
With the introduction of EasyRandom 4.0, it’s now possible to more easily customize the behaviour of the random test data by using the 3 newly introduced interfaces:
ExclusionPolicy
: Strategy interface for field/type exclusion (See #349)ObjectFactory
: Strategy interface for object creation (See #341)RandomizerProvider
: Strategy interface to provide randomizers for field/type (See #350)
In the following examples, I’ll demonstrate how to use these interfaces.
ExclusionPolicy
The ExclusionPolicy can be used to exclude fields based on the RandomizerContext. For example, it’s possible to exclude the name field for Product names but not for Owner names by using the following code:
data class Product(val name: String, val price: BigDecimal, val owner: Owner)
data class Owner(val name: String)
class ProductExclusionPolicy : ExclusionPolicy {
override fun shouldBeExcluded(field: Field, context: RandomizerContext): Boolean {
return field.name == "name" && context.targetType == Product::class.java
}
override fun shouldBeExcluded(type: Class<*>?, context: RandomizerContext?): Boolean {
return false
}
}
val parameters = EasyRandomParameters()
.exclusionPolicy(ProductExclusionPolicy())
val easyRandom = EasyRandom(parameters)
assertThat(easyRandom.nextObject(Product::class.java).name).isNull()
assertThat(easyRandom.nextObject(Owner::class.java)).isNotNull()
ObjectFactory
Another newly introduced interface is the ObjectFactory. The ObjectFactory is responsible for instantiating objects so that you can have more control over it. An example is shown below:
data class Product(val name: String, val price: BigDecimal)
class ProductFactory : ObjectFactory {
override fun <T : Any?> createInstance(type: Class<T>, context: RandomizerContext): T {
return if(type.isAssignableFrom(Product::class.java)) {
Product("test", BigDecimal("100")) as T
} else {
type.getDeclaredConstructor().newInstance()
}
}
}
val parameters = EasyRandomParameters()
.objectFactory(ProductFactory())
val easyRandom = EasyRandom(parameters)
assertThat(easyRandom.nextObject(Product::class.java).name).isEqualTo("test")
More examples can be found in the EasyRandom ObjectFactory JUnit tests.
RandomizerProvider
The last new interface is the RandomizerProvider. For an example on how to use this, have a look at the Github RandomizerProviderTest.
I was stuck converting the above code to Kotlin due to the wildcard generic, but with the help of Jacob Bass, we managed to convert it. Thanks Jacob for helping out here, much appreciated!
class ProductRandomizerProvider : RandomizerProvider {
override fun getRandomizerByField(field: Field, context: RandomizerContext): Randomizer<*>? {
if (field.name == "name" && context.currentRandomizationDepth == 0) {
return Randomizer { "product" }
}
if (field.name == "name" && context.currentField == "owner") {
return Randomizer { "owner" }
}
return null
}
}
val parameters = EasyRandomParameters()
.randomizerProvider(ProductRandomizerProvider())
val easyRandom = EasyRandom(parameters)
val product = easyRandom.nextObject(Product::class.java)
assertThat(product.name).isEqualTo("product")
assertThat(product.owner.name).isEqualTo("owner")
Conclusion
All in all, the release of EasyRandom 4.0 is a straightforward but welcome release. The alignment with the Jeasy libraries is a welcome one, and hopefully, we’ll see more great EasyRandom releases soon!