42

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?

Paulo Merson
  • 13,270
  • 8
  • 79
  • 72
Greg Konush
  • 572
  • 1
  • 5
  • 9
  • I think it is wrong that you use `@BeforeAll`. Why don't you use `@BeforeEach`? – Stefan Birkner Jul 22 '16 at 21:32
  • 3
    I was under impression that JUnit 5 instantiate test class only once, but it looks like it does it for each test case. Which make no difference between `@BeforeAll` and `@BeforeEach` ... almost no difference – Greg Konush Jul 22 '16 at 23:31

5 Answers5

45

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)
Ulises
  • 9,115
  • 2
  • 30
  • 27
  • 1
    It is also possible to define `PER_CLASS` as the default via a `junit-platform.properties` file, see Spring Kotlin documentation for more details: https://docs.spring.io/spring/docs/current/spring-framework-reference/languages.html#testing. – Sébastien Deleuze Mar 30 '18 at 13:34
  • 5
    Android Studio could not resolve PER_CLASS. `@TestInstance(TestInstance.Lifecycle.PER_CLASS)` – divonas Oct 22 '19 at 11:34
15

You have access to the variables inside the companion object:

    companion object {

        private lateinit var objectToBeInitialized: Test

        @BeforeAll
        @JvmStatic
        fun setup() {
            objectToBeInitialized = Test()
        }
    }
Eduardo Briguenti Vieira
  • 4,351
  • 3
  • 37
  • 49
9

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.

miensol
  • 39,733
  • 7
  • 116
  • 112
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() { }
}
Boris Treukhov
  • 17,493
  • 9
  • 70
  • 91
-1

In a @SpringBootTest, here's a simple solution. Notice that the @BeforeAll fun can use properties injected by Spring.

@SpringBootTest(classes = [Application::class])
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class MyTest {

    @Autowired
    lateinit var someBean: SomeBean

    lateinit var propertyForAll: PropertyForAll

    @BeforeAll
    fun setup() {
        propertyForAll = PropertyForAll(someBean.valueXyz())
    }

    @Test
    fun `First test`() {
        val something = propertyForAll.findSomething("Banana")
        // test continues
    }

    @Test
    fun `Second test`() {
        val something = propertyForAll.findSomething("Orange")
        // test continues
    }
}

Worth looking:

Paulo Merson
  • 13,270
  • 8
  • 79
  • 72