Showing posts with label JUnit. Show all posts
Showing posts with label JUnit. Show all posts

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.

2014-02-09

JUnit lifecycle

Introduction

JUnit is basic and first tool which is used for testing in Java. JUnit uses annotations to describe lifecycle, but sometimes order of operations executed by this library is not obvious. For example: when constructor of test class is called or when static initalization block is executed?

Main

This JUnit test class will be used to show when methods and blocks are executed.
package com.blogspot.przybyszd.junitlifecycle;

import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class JunitLifecycleTest{
    private static final Logger LOG = LoggerFactory.getLogger(JunitLifecycleTest.class);
    
    static{
        LOG.info("In static initialization block");
    }
    
    public JunitLifecycleTest(){
        LOG.info("In constructor");
    }
    
    {
        LOG.info("In initialization block");
    }
    
    @Test
    public void test1(){
        LOG.info("In test 1");
    }
    
    @Test
    public void test2(){
        LOG.info("In test 2");
    }
    
    @BeforeClass
    public static void oneTimeSetUp(){
        LOG.info("In before class");
    }
    
    @Before
    public void prepareTest(){
        LOG.info("In before");
    }
    
    @After
    public void tearDown(){
        LOG.info("In after");
    }
    
    @AfterClass
    public static void oneTimeTearDown(){
        LOG.info("In after class");
    }
}

And this is output after executing tests from this class.
2014-02-09 17:36:52,931 [INFO ] com.blogspot.przybyszd.junitlifecycle.JunitLifecycleTest:19 - In static initialization block
2014-02-09 17:36:52,935 [INFO ] com.blogspot.przybyszd.junitlifecycle.JunitLifecycleTest:42 - In before class
2014-02-09 17:36:52,936 [INFO ] com.blogspot.przybyszd.junitlifecycle.JunitLifecycleTest:27 - In initialization block
2014-02-09 17:36:52,937 [INFO ] com.blogspot.przybyszd.junitlifecycle.JunitLifecycleTest:23 - In constructor
2014-02-09 17:36:52,938 [INFO ] com.blogspot.przybyszd.junitlifecycle.JunitLifecycleTest:47 - In before
2014-02-09 17:36:52,938 [INFO ] com.blogspot.przybyszd.junitlifecycle.JunitLifecycleTest:32 - In test 1
2014-02-09 17:36:52,938 [INFO ] com.blogspot.przybyszd.junitlifecycle.JunitLifecycleTest:52 - In after
2014-02-09 17:36:52,940 [INFO ] com.blogspot.przybyszd.junitlifecycle.JunitLifecycleTest:27 - In initialization block
2014-02-09 17:36:52,941 [INFO ] com.blogspot.przybyszd.junitlifecycle.JunitLifecycleTest:23 - In constructor
2014-02-09 17:36:52,941 [INFO ] com.blogspot.przybyszd.junitlifecycle.JunitLifecycleTest:47 - In before
2014-02-09 17:36:52,941 [INFO ] com.blogspot.przybyszd.junitlifecycle.JunitLifecycleTest:37 - In test 2
2014-02-09 17:36:52,941 [INFO ] com.blogspot.przybyszd.junitlifecycle.JunitLifecycleTest:52 - In after
2014-02-09 17:36:52,942 [INFO ] com.blogspot.przybyszd.junitlifecycle.JunitLifecycleTest:57 - In after class

As we can see, static initialization block is executed before metchod annotated with BeforeClass. You can initialize here heavy objects, like Hibernate SessionFactory.

Before each test not only method annotated with Before is called. Earlier initialization block and constructor is called. There you can create resources, which should be not shared between tests (for example to get Hibernate Session from SessionFactory).

At the end of each test method annotated with After is called (for example to close Hibernate Session) and at the end of whole class, method annotated with AfterClass could free your resources (for example Hibernate SessionFactory).

Conclusion

It is why JUnit is great for unit testing. State for each test could be created in initial block, constructor of before method and it does not share it between test methods, because for each test new class is created. Of course, You can always store something in static variables, for example heavy objects which You want to create only once.