What is proper workaround for @BeforeAll in Kotlin

15,148

Solution 1

As stated in the documentation of @BeforeAll:

Denotes that the annotated method should be executed before all @Test methods in the current class; analogous to JUnit 4’s @BeforeClass. Such methods must be static and are inherited.

The above is true for both Kotlin and Java. Keep in mind that by default Junit will create a separate instance of a test class per test case. It makes sense that @BeforeAll will only work with static methods since it's supposed to be invoked before any code of current test case. A static method has no access to instance members because it can be invoked without an instance.

As stated in Spring documentation:

The "standaloneSetup" on the other hand is a little closer to a unit test.

The example shows that you should just use instance members like so:

class StandaloneTest {
  val smsController = ... // create instance of controller
  val MockMvcBuilders.standaloneSetup(smcController).build()
}

The usefulness of @BeforeAll is limited and should generally be avoided as it potentially encourages runtime dependencies between test cases.

Solution 2

JUnit 5 has @TestInstance(PER_CLASS) annotation that can be used for this purpose. One of the features that it enables is non-static BeforeAll and AfterAll methods:

@TestInstance(PER_CLASS)
class BeforeAllTests {

    lateinit var isInit = false

    @BeforeAll
    fun setup() {
        isInit = true
    }

   @TestFactory
   fun beforeAll() = listOf(
       should("initialize isInit in BeforeAll") {
           assertTrue(isInit)
       }
   )
}

fun should(name: String, test: () -> Unit) = DynamicTest.dynamicTest("should $name", test)

Solution 3

You have access to the variables inside the companion object:

    companion object {

        private lateinit var objectToBeInitialized: Test

        @BeforeAll
        @JvmStatic
        fun setup() {
            objectToBeInitialized = Test()
        }
    }

Solution 4

JUnit 5 supports test extensions, for example BeforeAllCallback

import org.junit.jupiter.api.extension.BeforeAllCallback
import org.junit.jupiter.api.extension.ExtensionContext

class BeforeAllTests : BeforeAllCallback {
    override fun beforeAll(context: ExtensionContext?) {
        System.out.println("Before all executed");
    }
}

In your Kotlin test class

@ExtendWith(BeforeAllTests::class)
class MyAppCoreTest : MyAppTest() {

    @Test
    fun someTest() { }
}
Share:
15,148
Greg Konush
Author by

Greg Konush

Updated on June 07, 2022

Comments

  • Greg Konush
    Greg Konush almost 2 years

    Currently the JUnit 5 API only allows @BeforeAll on a method that is static.

    So if I do something like this, it will not compile:

    @BeforeAll
      fun setup() {
        MockitoAnnotations.initMocks(this)
        mvc = MockMvcBuilders.standaloneSetup(controller).build()
    }
    

    In order to have a static method in Kotlin, I have to use companion object like this:

    companion object {
        @JvmStatic
        @BeforeAll
        fun setup() {
          MockitoAnnotations.initMocks(this)
          mvc = MockMvcBuilders.standaloneSetup(smsController).build()
        }
    }
    

    This will compile, but I don't have access to variables from the parent class. So what would be the idiomatic way to invoke JUnit 5 @BeforeAll with Kotlin?

  • Sébastien Deleuze
    Sébastien Deleuze about 6 years
    It is also possible to define PER_CLASS as the default via a junit-platform.properties file, see Spring Kotlin documentation for more details: docs.spring.io/spring/docs/current/spring-framework-referenc‌​e/….
  • Ulises
    Ulises about 6 years
    @SébastienDeleuze nice tip!
  • divonas
    divonas over 4 years
    Android Studio could not resolve PER_CLASS. @TestInstance(TestInstance.Lifecycle.PER_CLASS)
  • Shahab Rauf
    Shahab Rauf almost 3 years
    Perfect Answer.