Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is proper workaround for @BeforeAll in Kotlin

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?

like image 420
Greg Konush Avatar asked Jul 22 '16 00:07

Greg Konush


People also ask

What's wrong with @beforeall and @Afterall methods in Kotlin?

This behavior had a few drawbacks in certain scenarios. For example, it was impossible to declare @BeforeAll and @afterall on non-static methods since there was no cached instance against which those methods could be invoked. It also made it more difficult to implement @BeforeAll and @afterall methods in programming languages such as Kotlin.

Does Kotlin support JUnit 5 @beforeall and @Afterall annotations?

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

What's new with @beforeall and @Afterall methods?

Furthermore, since @BeforeAll and @afterall methods are no longer required to be static (if the test class is annotated with @testinstance (Lifecycle.PER_CLASS)), the following new use cases are now supported. - Declaration of @BeforeAll and @afterall methods in @nested test classes.

How to create non-static @beforeall and @Afterall methods in interface?

@BeforeAll and @AfterAll methods must be static and must not return a value. But in interface we can create non-static @BeforeAll and @AfterAll methods using @TestInstance annotation and those methods will be default methods.


3 Answers

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)
like image 93
Ulises Avatar answered Oct 17 '22 15:10

Ulises


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.

like image 42
miensol Avatar answered Oct 17 '22 16:10

miensol


You have access to the variables inside the companion object:

    companion object {

        private lateinit var objectToBeInitialized: Test

        @BeforeAll
        @JvmStatic
        fun setup() {
            objectToBeInitialized = Test()
        }
    }
like image 11
edubriguenti Avatar answered Oct 17 '22 15:10

edubriguenti