The object
keyword in Kotlin creates singleton in a very convenient way. It can be used for example as a state of an operation. Spock Framework is one of the most expressive and readable test frameworks available in the Java ecosystem. Let’s see how Kotlin object
can be used in the Spock tests.
What do we want to test?
We have a single method validate
in Validator
interface which returns validation status: Ok
or Error
.
sealed class ValidationStatus object Ok : ValidationStatus() object Error : ValidationStatus() interface Validator<T> { fun validate(value: T): ValidationStatus }
We also provide a simple implementation of this interface:
class AdultValidator : Validator<Int> { override fun validate(value: Int) = if (value >= 18) Ok else Error }
How to test it with Spock?
First – silly approach
First, let’s write a parameterized test for the validator:
AdultValidator sut = new AdultValidator() def 'should validate age #age'() { expect: sut.validate(age) == result where: age | result 0 | Error 17 | Error 18 | Ok 19 | Ok }
We expect it to pass, but it fails… Error
and Ok
are classes in the code above.
Second – naive approach
We need instances instead, so we modify the test a little:
def 'should validate age #age'() { expect: sut.validate(age) == result where: age | result 0 | new Error() 17 | new Error() 18 | new Ok() 19 | new Ok() }
And again, this one fails as well. Why? It is because Error
and Ok
classes do not have overridden equals
method. But why? We expects Kotlin objects (those created with object
keyword, not plain object) to have it implemented correctly. What is more, it works correctly in Kotlin:
fun isOk(status:ValidationStatus) = status == Ok
Third – correct approach
Let’s look into the class file:
$ javap com/github/alien11689/testingkotlinwithspock/Ok.class Compiled from "Validator.kt" public final class com.github.alien11689.testingkotlinwithspock.Ok extends com.github.alien11689.testingkotlinwithspock.ValidationStatus { public static final com.github.alien11689.testingkotlinwithspock.Ok INSTANCE; static {}; }
If we want to access the real object that Kotlin uses in such comparisson, then we should access the class static property called INSTANCE
:
def 'should validate age #age'() { expect: sut.validate(age) == result where: age | result 0 | Error.INSTANCE 17 | Error.INSTANCE 18 | Ok.INSTANCE 19 | Ok.INSTANCE }
Now the test passes.
Fourth – alternative approach
We can also check the method result without specific instance of the object class and use instanceof
or Class#isAssignableFrom
instead.
Show me the code
Code is available here.