Getting Started Easy Test Fixture Customization with Fixture Monkey

SeongAh Jo
NAVER Platform Labs
3 min readNov 14, 2023

Testing is an integral part of software development, and creating meaningful test fixtures is essential for writing effective tests. In this article, we’ll explore how Fixture Monkey, a versatile library for Kotlin and Java, simplifies the process of customizing properties in test instances. We’ll delve into a series of examples to showcase how Fixture Monkey effortlessly tailors test fixtures to your specific needs.

Introduction to Fixture Monkey

Fixture Monkey is a powerful library designed to make the creation of test fixtures in Kotlin and Java a breeze. It offers a clean and fluent API that allows developers to generate fixtures with ease, providing the flexibility to customize properties for different testing scenarios.

Setting Up Fixture Monkey

Before we dive into customization, let’s make sure Fixture Monkey is integrated into your project. You can include it in your Gradle project like this:

testImplementation("com.navercorp.fixturemonkey:fixture-monkey-starter-kotlin:1.0.1")

Now that we have Fixture Monkey in our project, let’s explore how it simplifies customizing properties.

Customizing Various Scenarios

The example tests below will use this instance of Fixture Monkey.

val fixtureMonkey = FixtureMonkey.builder().plugin(KotlinPlugin()).build()

Basic Customization

Let’s start with a basic scenario. Consider a class Foo with a string property. We want to create a test instance of Foo with a specific string for our test case.

@Test
fun customize() {
// given
class Foo(val string: String)

val expected = "expected"

// when
val actual = fixtureMonkey.giveMeBuilder<Foo>()
.setExp(Foo::string, expected)
.sample()
.string

// then
assertEquals(expected, actual)
}

Customizing Nested Property

Fixture Monkey supports customization of nested properties. Let’s look at an example with classes Foo and Bar:

@Test
fun customizeNestedProperty() {
// given
class Foo(val string: String)
class Bar(val foo: Foo)

val expected = "expected"

// when
val actual = fixtureMonkey.giveMeBuilder<Bar>()
.setExp(Bar::foo into Foo::string, expected)
.sample()
.foo
.string

// then
assertEquals(expected, actual)
}

Customizing Collection Element

Fixture Monkey makes it easy to customize elements within collections. Here’s an example with a class Foo containing a list:

@Test
fun customizeCollectionElement() {
// given
class Foo(val list: List<String>)

val expected = "expected"

// when
val actual = fixtureMonkey.giveMeBuilder<Foo>()
.sizeExp(Foo::list, 1)
.setExp(Foo::list[0], expected)
.sample()
.list[0]

// then
assertEquals(expected, actual)
}

Customizing Array Element

Customizing array elements is also straightforward with Fixture Monkey. Let’s consider a class `Foo` with an array property:

@Test
fun customizeArrayElement() {
// given
class Foo(val array: Array<String>)

val expected = "expected"

// when
val actual = fixtureMonkey.giveMeBuilder<Foo>()
.sizeExp(Foo::array, 1)
.setExp(Foo::array[0], expected)
.sample()
.array[0]

// then
assertEquals(expected, actual)
}

Customizing Map Key and Value

Customizing map keys and values is also supported by Fixture Monkey. Let’s consider a class Foo with a map property:

@Test
fun customizeMapKeyAndValue() {
// given
class Foo(val map: Map<String, Int>)

val expectedKey = "expectedKey"
val expectedValue = 42

// when
val actualKey = fixtureMonkey.giveMeBuilder<Foo>()
.setInner {
it.property("map") { m ->
m.size(1)
.key(expectedKey)
.value(expectedValue)
}
}
.sample()
.map
.keys.first()

val actualValue = FixtureMonkey.giveMeBuilder<Foo>()
.setInner {
it.property("map") { m ->
m.size(1)
.key(expectedKey)
.value(expectedValue)
}
}
.sample()
.map
.values.first()

// then
assertEquals(expectedKey, actualKey)
assertEquals(expectedValue, actualValue)
}

Setting Post-Conditions

Fixture Monkey also allows you to set post-conditions for generated instances. Here’s an example:

@Test
fun setPostCondition() {
// when
val actual = fixtureMonkey.giveMeBuilder<String>()
.setPostCondition { it.length < 5 }
.sample()

// then
assertThat(actual).hasSizeLessThan(5)
}

In this example, we set a post-condition to ensure that the generated string has a length less than 5. Fixture Monkey seamlessly incorporates this constraint into the generation process.

Conclusion

Fixture Monkey simplifies the often intricate process of creating meaningful test fixtures. Its fluent API and concise syntax make customization a straightforward task, whether you’re dealing with simple objects, nested properties, collections, arrays, or maps. In upcoming articles, we’ll explore more advanced features of Fixture Monkey, providing a deeper dive into its capabilities. Stay tuned for a comprehensive guide on leveraging Fixture Monkey to its fullest potential in your testing toolkit. Happy testing!

🔗 Github
📄 Documentation

--

--