Вступление
В 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 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 может быть изменен аннотацией, хотя, если класс теста аннотируется следующим образом:
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.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" ) } } } |
Сопутствующий объект 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.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 воздействовал на экземпляр теста (вставляет зависимости, разрешает свойства и т. Д.) Только после вызова аннотированного метода @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.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 способствовал моему пониманию нюансов JUnit 5 с Kotlin.
Смотреть оригинальную статью здесь: Kotlin и JUnit 5 @BeforeAll
Мнения, высказанные участниками Java Code Geeks, являются их собственными. |