Kotlin and JUnit 5 @BeforeAll
在Kotlin中,类没有静态方法。但是,可以使用伴随对象的概念向调用者提供Java等效语义。这篇文章将详细介绍支持JUnit 5 @BeforeAll和@AfterAll批注所需的工作,这取决于测试类中是否存在静态方法。
Java中的BeforeAll和AfterAll
Junit 5 @BeforeAll注释的方法在所有测试之前执行,@ AfterAll在所有测试之后执行。这些注释应应用于静态方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | 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"); } } |
大致的流程是:JUnit平台调用带注释的" @BeforeAll"方法,然后为每个测试 创建测试类的实例并调用测试。毕竟,将执行测试,然后将调用带有" @AfterAll"注释的静态方法。
这由日志证实。查看实例id(从Object的toString())与众不同:
1 2 3 4 5 6 | 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 |
但是,如果通过以下方式对测试类进行注释,则可以通过注释来更改JUnit 5测试的默认生命周期:
DIV>
1 2 3 4 | @TestInstance(TestInstance.Lifecycle.PER_CLASS) public class Junit5BeforeAllTest { .... } |
现在的优点是可以将@BeforeAll和@AfterAll批注放置在非静态方法上。但是要注意的是,在每次测试之前都不会重置任何实例级状态。
在Kotlin之前和之后
那么这如何转换成Kotlin?
对于每个测试一个新测试实例的默认情况,等效的Kotlin测试代码如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | 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") } } } |
带有@JvmStatic注释的方法的Kotlincompanion对象可以完成此工作。
修改生命周期的情况比较简单:
DIV>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | 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") } } |
我个人偏爱伴侣对象方法,因为我喜欢在执行测试方法之前确定测试实例的确定性状态的想法。该方法的另一个优势是基于Spring Boot的测试,您希望Spring仅在调用a @ BeforeAll-annotated方法之后对测试实例执行操作(注入依赖项,解析属性等)。为了更具体,请考虑以下示例:
DIV>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | 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 } |
如果将生命周期更改为" @TestInstance(TestInstance.Lifecycle.PER_CLASS)",则这种测试根本无法进行。
参考
这个StackOverflow答案有助于我理解Kotlin对JUnit 5的细微差别。 DIV>