The repository with the Kotlin code can be found on github/MisterDerpie/kotlin-data-stubs.
Currently I’m having the great luck that our team started using Kotlin at work. For that reason, I decided to learn Kotlin properly and started my journey with Kotlin in Action (2017, Manning). This is a bit outdated, considering that Kotlin 1.5 is the current release, but the vast majority of concepts are the same as for 1.0.
One concept that fascinates me and I really see a lot of value in is Extensions. Especially for tests I find that concept very useful, because it enables us to provide test related logic to our classes without actually adding this in the real production code. Let’s have a look at how we can utilize extensions to create stubs of business classes.
Problem statement in Java
Test Utils to Stub Classes
A problem that often arises in Java projects is that you need to provide stubs for using e.g. the same customer in multiple tests.
Suppose you have a class Customer.java
that looks like this
|
|
and you would like to stub the same customer for your tests.
How would you do that?
Probably you create some class called TestUtils.java
, that provides a static method stubCustomer
.
In larger projects, you most likely have many different stubs in it, from Address over Customer to Shopping Cart.
Thus, you start adding all these stubs in the TestUtils
class.
|
|
I have seen such classes having hundreds of lines of code, with many different, totally unrelated objects.
A first attempt to improve that would be to create TestUtilsCustomer.java
, TestUtilsAddress.java
, …
That would leave us with a better, yet still not the best solution.
Test Code meets Production Code
Wouldn’t it be nice to provide stubCustomer
directly in the Customer
itself?
With Java, you would have to include such method in your production code.
|
|
Testcode should never, never reside in production code.
With Kotlin, we can actually do that, but without production code ever knowing about this stubFullAgeCustomer
method.
Solution in Kotlin
Before continue reading, think about why it would be nice to have a preconfigured Customer provided by the Customer
object itself.
When implementing the Builder Pattern, you configure each property by a callchain of functions with the properties’ names.
It could look like this Customer.name("Builder").age(23).uuid(UUID.randomUUID()).build()
.
So why not getting a fully preset instance as part of this object, too?
This can be achieved in Kotlin.
Setup
We define a Customer
data class and an AgeCheckService
(the latter is purely to have something to test, so that I can show how our extension works).
Customer.kt
|
|
What enables us to extend the Customer
with new “static” methods for our test is the companion object.
As this is a pure data class, we don’t add anything else.
AgeCheckService.kt
|
|
This services only functionality is to return true
when the customer is of full age, and false
when they’re minor.
As initially stated, this is for the sole purpose of having something to test.
Extend Customer
In our test, we would like to call the following:
|
|
to retrieve preconfigured customers with some age greater or equal than 18 or less than 18, respectively. This is as easy as doing companion object extensions.
To do so, simply place a file in your testpath, and define the functions on Customer.Companion
.
|
|
Note that when you do not define a name for the companion object in the class, it will be accessible by Companion
.
Congratulations, you just added extensions which you can use in your tests.
Test AgeCheckService using Extension Stub
The last step is to import your Extensions into your test. They are not automatically applied globally to your Customer, which is the reason why you explicitly have to import them.
|
|
See the imports at the top.
I placed the Customer
in a package called dto
.
The stubFullAgeCustomer
and stubMinorAgeCustomer
are also placed in a package called dto
(but the name doesn’t matter, it’s just due to the same folder structure in the tests).
As stated initially, we need to import them to actually apply the extension to the Customer
.
The tests are then straightforward: Get the customer and assert that the age service in fact returns true
when they’re of full age and false
when they’re of minor age.