Вступление
В Kotlin у классов нет статических методов . Java-эквивалентная семантика может быть предоставлена вызывающим абонентам, используя концепцию сопутствующего объекта . В этом посте будет подробно рассказано о том, что требуется для поддержки аннотаций JUnit 5 @BeforeAll и @AfterAll, которые зависят от прецедента статических методов в тестовых классах.
До и после и все в Java
Junit 5 @BeforeAll аннотированные методы выполняются до всех тестов, а @AfterAll ожидается после всех тестов. Предполагается, что эти аннотации будут применяться к статическим методам:
|
01
02
03
04
05
06
07
08
09
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», это подтверждается журналами, посмотрите, как отличаются идентификаторы экземпляра (из toString () объекта):
|
1
2
3
4
5
6
|
2018-03-28 17:22:03.618 INFO --- [ main] c.p.cookbook.Junit5BeforeAllTest : beforeAll called2018-03-28 17:22:03.652 INFO --- [ main] c.p.cookbook.Junit5BeforeAllTest : aTest1 called2018-03-28 17:22:03.653 INFO --- [ main] c.p.cookbook.Junit5BeforeAllTest : com.pivotalservices.cookbook.Junit5BeforeAllTest@7bc1a03d2018-03-28 17:22:03.663 INFO --- [ main] c.p.cookbook.Junit5BeforeAllTest : aTest2 called2018-03-28 17:22:03.664 INFO --- [ main] c.p.cookbook.Junit5BeforeAllTest : com.pivotalservices.cookbook.Junit5BeforeAllTest@6591f5172018-03-28 17:22:03.669 INFO --- [ main] c.p.cookbook.Junit5BeforeAllTest : afterAll called |
Этот жизненный цикл по умолчанию теста JUnit 5 может быть изменен аннотацией, хотя, если класс теста аннотируется следующим образом:
|
1
2
3
4
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)public class Junit5BeforeAllTest {....} |
Преимущество теперь состоит в том, что аннотации @BeforeAll и @AfterAll можно размещать в нестатических методах, поскольку платформа JUnit 5 может гарантировать, что эти методы будут ровно один раз перед ВСЕМИ тестами. Суть в том, что любое состояние уровня экземпляра не будет сбрасываться перед каждым тестом.
До и после и все в Котлине
Так как же это перевести на Kotlin —
Для случая по умолчанию нового экземпляра теста для каждого теста эквивалентный код теста Kotlin выглядит следующим образом:
|
01
02
03
04
05
06
07
08
09
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.AfterAllimport org.junit.jupiter.api.BeforeAllimport org.junit.jupiter.api.Testimport org.slf4j.LoggerFactoryclass 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") } }} |
Сопутствующий объект Kotlin с методами, аннотированными @JvmStatic, выполняет эту работу.
Упрощен тот случай, когда жизненный цикл изменяется:
|
01
02
03
04
05
06
07
08
09
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.AfterAllimport org.junit.jupiter.api.BeforeAllimport org.junit.jupiter.api.Testimport org.junit.jupiter.api.TestInstanceimport 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 воздействовал на экземпляр теста (вставляет зависимости, разрешает свойства и т. Д.) Только после вызова аннотированного метода @BeforeAll, чтобы конкретнее рассмотреть следующий пример:
|
01
02
03
04
05
06
07
08
09
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.assertThatimport org.junit.jupiter.api.AfterAllimport org.junit.jupiter.api.BeforeAllimport org.junit.jupiter.api.Testimport org.junit.jupiter.api.extension.ExtendWithimport org.springframework.beans.factory.annotation.Valueimport org.springframework.boot.test.context.SpringBootTestimport org.springframework.context.annotation.Configurationimport org.springframework.test.context.junit.jupiter.SpringExtension@ExtendWith(SpringExtension::class)@SpringBootTestclass 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 способствовал моему пониманию нюансов JUnit 5 с Kotlin.
| Смотреть оригинальную статью здесь: Kotlin и JUnit 5 @BeforeAll
Мнения, высказанные участниками Java Code Geeks, являются их собственными. |