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

Wednesday, March 28, 2018

Kotlin and JUnit 5 @BeforeAll

Introduction

In Kotlin, classes do not have static methods. A Java equivalent semantic can be provided using the concept of a companion object though. This post will go into details of what it takes to support a JUnit 5 @BeforeAll and @AfterAll annotation which depend on the presence of a static methods in test classes.


BeforeAll and AfterAll in Java

Junit 5 @BeforeAll annotated methods are executed before all tests and @AfterAll is exected after all tests. These annotations are expected to be applied to static methods:

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Junit5BeforeAllTest {

    private static final Logger LOGGER = LoggerFactory.getLogger(Junit5BeforeAllTest.class);
    
    @BeforeAll
    static void beforeAll() {
        LOGGER.info("beforeAll called");    
    }
    
    @Test
    public void aTest1() {
        LOGGER.info("aTest1 called");
        LOGGER.info(this.toString());        
    }
    
    @Test
    public void aTest2() {
        LOGGER.info("aTest2 called");
        LOGGER.info(this.toString());
    }
    
    @AfterAll
    static void afterAll() {
        LOGGER.info("afterAll called");        
    }
}

A rough flow is - the JUnit platform calls the "@BeforeAll" annotated methods, then for each test it creates an instance of the test class and invokes the test. After all tests are executed, the "@AfterAll" annotated static methods are called, this is borne out by the logs, see how the instance ids(from toString() of Object) is different:

2018-03-28 17:22:03.618  INFO   --- [           main] c.p.cookbook.Junit5BeforeAllTest         : beforeAll called
2018-03-28 17:22:03.652  INFO   --- [           main] c.p.cookbook.Junit5BeforeAllTest         : aTest1 called
2018-03-28 17:22:03.653  INFO   --- [           main] c.p.cookbook.Junit5BeforeAllTest         : com.pivotalservices.cookbook.Junit5BeforeAllTest@7bc1a03d
2018-03-28 17:22:03.663  INFO   --- [           main] c.p.cookbook.Junit5BeforeAllTest         : aTest2 called
2018-03-28 17:22:03.664  INFO   --- [           main] c.p.cookbook.Junit5BeforeAllTest         : com.pivotalservices.cookbook.Junit5BeforeAllTest@6591f517
2018-03-28 17:22:03.669  INFO   --- [           main] c.p.cookbook.Junit5BeforeAllTest         : afterAll called



This default lifecycle of a JUnit 5 test can be changed by an annotation though if the test class is annotated the following way:

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class Junit5BeforeAllTest {
....
}

The advantage now is that the @BeforeAll and @AfterAll annotations can be placed on non-static methods, as the JUnit 5 platform can guarantee that these methods are exactly once before ALL tests. The catch though is that any instance-level state will not be reset before each test.

BeforeAll and AfterAll in Kotlin

So how does this translate to Kotlin -
For the default case of a new test instance per test, an equivalent Kotlin test code looks like this:

import org.junit.jupiter.api.AfterAll
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.Test
import org.slf4j.LoggerFactory

class Junit5BeforeAllKotlinTest {

    @Test
    fun aTest1() {
        LOGGER.info("aTest1 called")
        LOGGER.info(this.toString())
    }

    @Test
    fun aTest2() {
        LOGGER.info("aTest2 called")
        LOGGER.info(this.toString())
    }

    companion object {
        private val LOGGER = LoggerFactory.getLogger(Junit5BeforeAllTest::class.java)


        @BeforeAll
        @JvmStatic
        internal fun beforeAll() {
            LOGGER.info("beforeAll called")
        }

        @AfterAll
        @JvmStatic
        internal fun afterAll() {
            LOGGER.info("afterAll called")
        }
    }
}

A Kotlin companion object with methods annotated with @JvmStatic does the job.

Simpler is the case where the lifecycle is modified:

import org.junit.jupiter.api.AfterAll
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
import org.slf4j.LoggerFactory

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class Junit5BeforeAllKotlinTest {

    private val LOGGER = LoggerFactory.getLogger(Junit5BeforeAllTest::class.java)

    @BeforeAll
    internal fun beforeAll() {
        LOGGER.info("beforeAll called")
    }

    @Test
    fun aTest1() {
        LOGGER.info("aTest1 called")
        LOGGER.info(this.toString())
    }

    @Test
    fun aTest2() {
        LOGGER.info("aTest2 called")
        LOGGER.info(this.toString())
    }


    @AfterAll
    internal fun afterAll() {
        LOGGER.info("afterAll called")
    }
}


My personal preference is for the companion object approach as I like the idea of a deterministic state of the test instance before the test method is executed. Another advantage of the approach is with Spring Boot based tests where you want Spring to act on the test instance (inject dependencies, resolve properties etc) only after @BeforeAll annotated method is called, to make this more concrete consider the following example:

import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.AfterAll
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.beans.factory.annotation.Value
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.context.annotation.Configuration
import org.springframework.test.context.junit.jupiter.SpringExtension


@ExtendWith(SpringExtension::class)
@SpringBootTest
class BeforeAllSampleTest {

    @Value("\${some.key}")
    private lateinit var someKey: String

    
    companion object {
        @BeforeAll
        @JvmStatic
        fun beforeClass() {
            System.setProperty("some.key", "some-value")
        }

        @AfterAll
        @JvmStatic
        fun afterClass() {
            System.clearProperty("some.key")
        }
    }

    @Test
    fun testValidateProperties() {
        assertThat(someKey).isEqualTo("some-value")
    }

    @Configuration
    class SpringConfig
}

This kind of a test will not work at all if the lifecycle were changed to "@TestInstance(TestInstance.Lifecycle.PER_CLASS)"

Correction
Per comments from the one and only Sébastien Deleuze, the previous test can be simplified by injecting in dependencies and properties via constructor injection, so the test can be re-written as:

@ExtendWith(SpringExtension::class)
@SpringBootTest
class BeforeAllSampleTest(@Value("\${some.key}") val someKey: String) {
 
    companion object {
        @BeforeAll
        @JvmStatic
        fun beforeClass() {
            System.setProperty("some.key", "some-value")
        }
 
        @AfterAll
        @JvmStatic
        fun afterClass() {
            System.clearProperty("some.key")
        }
    }
 
    @Test
    fun testValidateProperties() {
        assertThat(someKey).isEqualTo("some-value")
    }
 
    @Configuration
    class SpringConfig
}

Reference

This stackoverflow answer was instrumental in my understanding of the nuances of JUnit 5 with Kotlin.

Thursday, January 4, 2018

Spring Based Application - Migrating to Junit 5

This is a quick write-up on migrating a Gradle based Spring Boot app from Junit 4 to the shiny new Junit 5. Junit 4 tests continue to work with Junit 5 Test Engine abstraction which provides support for tests written in different programming models, in this instance, Junit 5 supports a Vintage Test Engine with the ability to run JUnit 4 tests.


Here is a sample project with JUnit 5 integrations already in place along with sample tests in Junit 4 and Junit 5 - https://github.com/bijukunjummen/boot2-with-junit5-sample

Sample Junit 4 candidate test

As a candidate project, I have a Spring Boot 2 app with tests written in Kotlin using Junit 4 as the testing framework. This is how a sample test looks with all dependencies explicitly called out. It uses the Junit4's @RunWith annotation to load up the Spring Context:

import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
import org.junit.runner.RunWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest
import org.springframework.test.context.junit4.SpringRunner
import org.springframework.test.web.reactive.server.WebTestClient
import java.nio.charset.StandardCharsets

@RunWith(SpringRunner::class)
@WebFluxTest(controllers = arrayOf(RouteConfig::class))
class SampleJunit4Test {

    @Autowired
    lateinit var webTestClient: WebTestClient

    @Test
    fun `get of hello URI should return Hello World!`() {
        webTestClient.get()
                .uri("/hello")
                .exchange()
                .expectStatus().isOk
                .expectBody()
                .consumeWith({ m ->
                    assertThat(String(m.responseBodyContent, StandardCharsets.UTF_8)).isEqualTo("Hello World!")
                })

    }

}

the Junit 4 dependencies are pulled in transitively via "spring-boot-starter-test" module:

testCompile('org.springframework.boot:spring-boot-starter-test')


Junit 5 migration


The first step to do is to pull in the Junit 5 dependencies along with Gradle plugin which enables running the tests:

Plugin:

buildscript {
 dependencies {
  ....
  classpath 'org.junit.platform:junit-platform-gradle-plugin:1.0.2'
 }
}
apply plugin: 'org.junit.platform.gradle.plugin'

Dependencies:

testCompile("org.junit.jupiter:junit-jupiter-api")
testRuntime("org.junit.jupiter:junit-jupiter-engine")
testRuntime("org.junit.vintage:junit-vintage-engine:4.12.2")

With these changes in place, all the Junit 4 tests will continue to run both in IDE and when the Gradle build is executed and at this point, the tests itself can be slowly migrated over.

The test which I had shown before looks like this with Junit 5 Jupiter which provides the programming model for the tests:

import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.extension.ExtendWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.reactive.WebFluxTest
import org.springframework.test.context.junit.jupiter.SpringExtension
import org.springframework.test.web.reactive.server.WebTestClient
import java.nio.charset.StandardCharsets

@ExtendWith(SpringExtension::class)
@WebFluxTest(controllers = arrayOf(RouteConfig::class))
class SampleJunit5Test {

    @Autowired
    lateinit var webTestClient: WebTestClient

    @Test
    fun `get of hello URI should return Hello World!`() {
        webTestClient.get()
                .uri("/hello")
                .exchange()
                .expectStatus().isOk
                .expectBody()
                .consumeWith({ m ->
                    assertEquals("Hello World!", String(m.responseBodyContent, StandardCharsets.UTF_8))
                })
    }

}

Note that now instead of using JUnit 4 @RunWith annotation, I am using the @ExtendWith annotation and providing SpringExtension as a parameter which is responsible for loading up the Spring Context like before. Rest of the Spring annotations will continue to work with JUnit 5. This way tests can be slowly moved over from JUnit 4 to JUnit 5.


Caveats

Not everything is smooth though, there are a few issues in migrating from JUnit 4 to JUnit 5, the biggest of them is likely the support for JUnit @Rule and @ClassRule annotation and the JUnit 5 documentation does go into details on how it can be mitigated.

Thursday, February 5, 2015

Netflix Governator Tests - Introducing governator-junit-runner

Consider a typical Netflix Governator junit test.

public class SampleWithGovernatorJunitSupportTest {

    @Rule
    public LifecycleTester tester = new LifecycleTester();

    @Test
    public void testExampleBeanInjection() throws Exception {
        tester.start();
        Injector injector = tester
                .builder()
                .withBootstrapModule(new SampleBootstrapModule())
                .withModuleClass(SampleModule.class)
                .usingBasePackages("sample.gov")
                .build()
                .createInjector();

        BlogService blogService = injector.getInstance(BlogService.class);
        assertThat(blogService.get(1l), is(notNullValue()));
        assertThat(blogService.getBlogServiceName(), equalTo("Test Blog Service"));
    }

}

This test is leveraging the Junit rule support provided by Netflix Governator and tests some of the feature sets of Governator - Bootstrap modules, package scanning, configuration support etc.

The test however has quite a lot of boilerplate code which I felt could be reduced by instead leveraging a Junit Runner type model. As a proof of this concept, I am introducing the unimaginatively named project - governator-junit-runner, consider now the same test re-written using this library:

@RunWith(GovernatorJunit4Runner.class)
@LifecycleInjectorParams(modules = SampleModule.class, bootstrapModule = SampleBootstrapModule.class, scannedPackages = "sample.gov")
public class SampleGovernatorRunnerTest {

    @Inject
    private BlogService blogService;

    @Test
    public void testExampleBeanInjection() throws Exception {
        assertNotNull(blogService.get(1l));
        assertEquals("Test Blog Service", blogService.getBlogServiceName());
    }

}

Most of the boilerplate is now implemented within the Junit runner and the parameters required to bootstrap Governator is passed in through the LifecycleInjectorParams annotation. The test instance itself is a bound component and thus can be injected into, this way the instances which need to be tested can be injected into the test itself and asserted on. If you want more fine-grained control, the LifecycleManager itself can be injected into the test!:

@Inject
private Injector injector;

@Inject
private LifecycleManager lifecycleManager;

If this interests you, more samples are at the project site here.

Tuesday, January 8, 2013

JUnit test method ordering

Junit until version 4.10 uses the order of test methods in a test class as returned by the reflection API as the order of test method execution - Class.getMethods(). To quote the Javadoc of getMethods() api:

The elements in the array returned are not sorted and are not in any particular order.

thus the order of test method execution in a test class is not predictable. This in a way is good as it encourages us as developers to write test methods which can stand on its own and to not depend on the order of test method execution.

With version 4.11 of Junit the order of test method execution is not so unpredictable anymore, by default the order though not specified will be deterministic for every run. The order can further be enforced by adding a new annotation @FixMethodOrder to the test class with the following values:

1. @FixMethodOrder(MethodSorters.DEFAULT) - deterministic order based on an internal comparator
2. @FixMethodOrder(MethodSorters.NAME_ASCENDING) - ascending order of method names
3. @FixMethodOrder(MethodSorters.JVM) - pre 4.11 way of depending on reflection based order

Consider the following test case:

public class ATest {
 @Test
 public void b_test_1() {
  System.out.println("b_test_1 called..");
 }

 @Test
 public void r_test_2() {
  System.out.println("r_test_2 called..");
 }

 @Test
 public void z_test_3() {
  System.out.println("z_test_3 called..");
 }


 @Test
 public void l_test_4() {
  System.out.println("l_test_4 called..");
 }
}
Pre 4.11 running this test prints the following in my machine

Running testorder.ATest
r_test_2 called..
z_test_3 called..
b_test_1 called..
l_test_4 called..

With NAME_ASCENDING:
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class ATest

this is the output:

Running testorder.ATest
b_test_1 called..
l_test_4 called..
r_test_2 called..
z_test_3 called..


There is still no way to explicitly specify other custom sorting methods which is good as the purpose of providing a predictable order is just that - to make it predictable, not to use it to add dependencies between test methods.

Reference:
JUnit Wiki - https://github.com/KentBeck/junit/wiki/Test-execution-order