Showing posts with label Spock. Show all posts
Showing posts with label Spock. Show all posts

2018-09-27

Testing Kotlin with Spock Part 3 - Interface default method

Kotlin allows you to put method implementation in an interface. The same mechanism can be found in Java interfaces as default methods (and also Groovy or Scala traits). Let's see the difference between the Kotlin and Java default methods in interface by testing it with Groovy and Spock.

What do we want to test?

We often have an interface for access object from the database. In domain, they might look similar to this KotlinOrderRepository:

interface KotlinOrderRepository {
    fun save(order: Order)

    fun find(orderId: OrderId): Order?

    fun get(orderId: OrderId): Order =
            find(orderId) ?: throw NotFound()
}

How to fake it with Groovy?

When we want to use such interface in tests, we can, of course, mock it. However, it is far better to fake repositories with a simple, in-memory implementation. Let's create FakeKotlinOrderRepository in Groovy:

class FakeKotlinOrderRepository implements KotlinOrderRepository {
    private Map<OrderId, Order> data = [:]

    @Override
    void save(Order order) {
        data[order.id] = order
    }

    @Override
    Order find(OrderId orderId) {
        return data[orderId]
    }
}

Unfortunately, this causes a compilation error

/testing-kotlin-in-spock/src/test/groovy/com/github/alien11689/testingkotlinwithspock/defaultmethod/FakeKotlinOrderRepository.groovy: 3: Can't have an abstract method in a non-abstract class. The class 'com.github.alien11689.testingkotlinwithspock.defaultmethod.FakeKotlinOrderRepository' must be declared abstract or the method 'com.github.alien11689.testingkotlinwithspock.defaultmethod.Order get(com.github.alien11689.testingkotlinwithspock.defaultmethod.OrderId)' must be implemented.
 @ line 3, column 1.
   class FakeKotlinOrderRepository implements KotlinOrderRepository {
   ^

1 error

The compiler doesn't see the implementation of the get method in the Kotlin interface. We have to use some magic to make it work in groovy.

Solution

To solve the problem, let's look into the generated classes:

$ ls build/classes/main/com/github/alien11689/testingkotlinwithspock/defaultmethod/
JavaOrderRepository.class
KotlinOrderRepository.class
KotlinOrderRepository$DefaultImpls.class
NotFound.class
Order.class
OrderId.class

The KotlinOrderRepository$DefaultImpls class is the one we're looking for as we can use it in Groovy to implement the missing operation.

class FakeKotlinOrderRepository implements KotlinOrderRepository {

    // ...

    Order get(OrderId orderId) {
        return KotlinOrderRepository.DefaultImpls.get(this, orderId)
    }
}

Now the code compiles and tests pass:

class KotlinRepositoryWithDefaultMethodTest extends Specification {
    OrderId orderId = new OrderId(UUID.randomUUID() as String)
    Order order = new Order(orderId, 'data')
    KotlinOrderRepository kotlinOrderRepository = new FakeKotlinOrderRepository()

    def 'should get order from kotlin repository'() {
        given:
            kotlinOrderRepository.save(order)
        expect:
            kotlinOrderRepository.get(orderId) == order
    }

    def 'should throw NotFound when order does not exist in kotlin repository'() {
        when:
            kotlinOrderRepository.get(orderId)
        then:
            thrown(NotFound)
    }
}

Is there the same problem with Java?

Let's have a quick look at how this works with Java interfaces. If we write a similar repository in Java:

public interface JavaOrderRepository {
    void save(Order order);

    Optional<Order> find(OrderId orderId);

    default Order get(OrderId orderId) {
        return find(orderId).orElseThrow(NotFound::new);
    }
}

and create a fake implementation in Groovy:

class FakeJavaOrderRepository implements JavaOrderRepository {
    private Map<OrderId, Order> data = [:]

    @Override
    void save(Order order) {
        data[order.id] = order
    }

    @Override
    Optional<Order> find(OrderId orderId) {
        return Optional.ofNullable(data[orderId])
    }
}

there is no compilation error and the tests pass:

class JavaRepositoryWithDefaultMethodTest extends Specification {
    OrderId orderId = new OrderId(UUID.randomUUID() as String)
    Order order = new Order(orderId, 'data')
    JavaOrderRepository javaOrderRepository = new FakeJavaOrderRepository()

    def 'should get order from java repository'() {
        given:
            javaOrderRepository.save(order)
        expect:
            javaOrderRepository.get(orderId) == order
    }

    def 'should throw NotFound when order does not exist in java repository'() {
        when:
            javaOrderRepository.get(orderId)
        then:
            thrown(NotFound)
    }
}

Groovy can implement Java interfaces with the default methods without any problems.

Show me the code

Code is available here.

2018-05-28

Testing Kotlin with Spock Part 2 - Enum with instance method

The enum class with instance method in Kotlin is quite similar to its Java version, but they are look a bit different in the bytecode. Let's see the difference by writing some tests using Spock.


What do we want to test?

Let's see the code that we want to test:
enum class EnumWithInstanceMethod {
    PLUS {
        override fun sign(): String = "+"
    },
    MINUS {
        override fun sign(): String = "-"
    };

    abstract fun sign(): String
}
Obviously, it can be written in a better way (e. g. using enum instance variable), but this example shows the case we want to test in the simplest way.


How to test it with Spock?

The simplest test (that does not work)

First, we can write the test like we would do it with a Java enum:
def "should use enum method like in java"() {
    expect:
        EnumWithInstanceMethod.MINUS.sign() == '-'
}
The test fails:
Condition failed with Exception:

EnumWithInstanceMethod.MINUS.sign() == '-'
                             |
                             groovy.lang.MissingMethodException: No signature of method: static com.github.alien11689.testingkotlinwithspock.EnumWithInstanceMethod$MINUS.sign() is applicable for argument types: () values: []
                             Possible solutions: sign(), sign(), is(java.lang.Object), find(), with(groovy.lang.Closure), find(groovy.lang.Closure)


    at com.github.alien11689.testingkotlinwithspock.EnumWithInstanceMethodTest.should use enum method like in java(EnumWithInstanceMethodTest.groovy:11)
Caused by: groovy.lang.MissingMethodException: No signature of method: static com.github.alien11689.testingkotlinwithspock.EnumWithInstanceMethod$MINUS.sign() is applicable for argument types: () values: []
Possible solutions: sign(), sign(), is(java.lang.Object), find(), with(groovy.lang.Closure), find(groovy.lang.Closure)
    ... 1 more
Interesting... Why is Groovy telling us that we are trying to call a static method? Maybe we are not using the enum instance but something else?. Let's create a test where we pass the enum instance to method:
static String consume(EnumWithInstanceMethod e) {
    return e.sign()
}

def "should pass enum as parameter"() {
    expect:
        consume(EnumWithInstanceMethod.MINUS) == '-'
}
Error message:
Condition failed with Exception:

consume(EnumWithInstanceMethod.MINUS) == '-'
|
groovy.lang.MissingMethodException: No signature of method: static com.github.alien11689.testingkotlinwithspock.EnumWithInstanceMethodTest.consume() is applicable for argument types: (java.lang.Class) values: [class com.github.alien11689.testingkotlinwithspock.EnumWithInstanceMethod$MINUS]
Possible solutions: consume(com.github.alien11689.testingkotlinwithspock.EnumWithInstanceMethod)


    at com.github.alien11689.testingkotlinwithspock.EnumWithInstanceMethodTest.should pass enum as parameter(EnumWithInstanceMethodTest.groovy:29)
Caused by: groovy.lang.MissingMethodException: No signature of method: static com.github.alien11689.testingkotlinwithspock.EnumWithInstanceMethodTest.consume() is applicable for argument types: (java.lang.Class) values: [class com.github.alien11689.testingkotlinwithspock.EnumWithInstanceMethod$MINUS]
Possible solutions: consume(com.github.alien11689.testingkotlinwithspock.EnumWithInstanceMethod)
    ... 1 more
Now we see that we passed the class com.github.alien11689.testingkotlinwithspock.EnumWithInstanceMethod$MINUS, not the enum instance.


But it works in Java...

Analogous code in JUnit works perfectly and the test passes:
@Test
public void shouldReturnSign() {
    assertEquals("-", EnumWithInstanceMethod.MINUS.sign());
}
Java can access Kotlin's instance method without problems, so maybe something is wrong with Groovy...
But the Java enum with instance method, e. g.
public enum EnumWithInstanceMethodInJava {
    PLUS {
        public String sign() {
            return "+";
        }
    },
    MINUS {
        public String sign() {
            return "-";
        }
    };

    public abstract String sign();
}
works correctly in the Spock test:
def "should use enum method"() {
    expect:
        EnumWithInstanceMethodInJava.MINUS.sign() == '-'
}


What's the difference?

We can spot the difference just by looking at the compiled classes:
$ tree build/classes/main/
build/classes/main/
└── com
    └── github
        └── alien11689
            └── testingkotlinwithspock
                ├── AdultValidator.class
                ├── EnumWithInstanceMethod.class
                ├── EnumWithInstanceMethodInJava$1.class
                ├── EnumWithInstanceMethodInJava$2.class
                ├── EnumWithInstanceMethodInJava.class
                ├── EnumWithInstanceMethod$MINUS.class
                ├── EnumWithInstanceMethod$PLUS.class
                ├── Error.class
                ├── Ok.class
                ├── ValidationStatus.class
                └── Validator.class
Java generates anonymous classes (EnumWithInstanceMethodInJava$1 and EnumWithInstanceMethodInJava$2) for the enum instances, but Kotlin names those classes after the enum instances names (EnumWithInstanceMethod$MINUS and EnumWithInstanceMethod$PLUS).
How does it tie into the problem with Groovy? Groovy does not need the .class when accessing class in code, so when we try to access EnumWithInstanceMethod.MINUS, Groovy converts it to EnumWithInstanceMethod.MINUS.class, not the instance of the enum. The same problem does not occur in Java code since there is no EnumWithInstanceMethodInJava$MINUS class.


Solution

Knowing the difference, we can solve the problem of accessing Kotlin's enum instance in our Groovy code.
The first solution is accessing the enum instance with valueOf method:
def "should use enum method working"() {
    expect:
        EnumWithInstanceMethod.valueOf('MINUS').sign() == '-'
}
The second way is to tell Groovy explicitly that we want to access the static field which is the instance of enum:
def "should use enum method"() {
    expect:
        EnumWithInstanceMethod.@MINUS.sign() == '-'
}
You can choose either solution depending on style of your code and your preferences.


Show me the code

Code is available here.

2018-03-13

Testing Kotlin with Spock Part 1 - Object

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 framework 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.

def 'should validate age #age'() {
    when:
        ValidationStatus result = sut.validate(age)
    then:
        result.class.isAssignableFrom(expected)
    where:
        age | expected
        0   | Error
        17  | Error
        18  | Ok
        19  | Ok
}

Show me the code

Code is available here.

2015-09-07

Groovy, Callable and ExecutorService

Suppose you want submit job to ExecutorService.

The Baroque version

You could create a class that implements Callable:
class MyJob implements Callable<Integer>{
    @Override
    Integer call() throws Exception {
        return 42
    }
}

and give it to the executor service:

def 'submit callable as MyJob object'() {
    expect:
        executorService.submit(new MyJob()).get() == 42
}

The response is, as expected, 42.

Map as Callable version

You want to use this job only in one place so why not inline this class:
def 'submit callable as map'() {
    expect:
        executorService.submit([call: { 42 }] as Callable).get() == 42
}

The response is again 42.

Groovy closure version

Why not use closure instead of map?
def 'submit callable as closure'(){
    expect:
        executorService.submit { 42 }.get() == 42
}

The response is ... null.

Condition not satisfied:
executorService.submit { 42 }.get() == 42
|               |             |     |
|               |             null  false
|               java.util.concurrent.FutureTask@21de60b4
java.util.concurrent.Executors$FinalizableDelegatedExecutorService@1700915

Why? It is because Groovy treats this closure as Runnable, not Callable and Future#get returns null when task is complete.

Groovy closure version with cast

We have to cast our closure before submiting to executor service:
def 'submit callable as closure with cast'() {
    when:
        int result = executorService.submit({ return 42 } as Callable<Integer>).get()
    then:
        result == 42
}

The response is, as expected, again 42.

What interesting, the same test with inlined result variable fails... Strange... It could be Spock framework error.

Source code is available here.

2014-04-11

Spock test template for IntelliJ IDEA

Spock tests should have specific structure. I generally use one form of test: with given, when, then and optional where blocks. I also like to have all cases visible, so I add Unroll annotation. I need simple way to generate test in this way.

I am developing using IntelliJ IDEA, so I describe how to configure live template using this IDE. Select File->Settings...->Live Templates. Now add new template group and add new template inside this. Abbreviation set to something meaningful and short, for example spock. As template text use code below:
@spock.lang.Unroll
def "should $END$"(){
    given:
    
    when:
    
    then:
    
    where:
    
}
Set also option "Reformat according to style", apllicable set to groovy and choose expand key.

Now, when you type spock and click expand key (in my case it is Tab) in editor, then spock will be replaced with template and coursor will be set after space in test method name.