O slideshow foi denunciado.
Utilizamos seu perfil e dados de atividades no LinkedIn para personalizar e exibir anúncios mais relevantes. Altere suas preferências de anúncios quando desejar.

JUnit5 and TestContainers

298 visualizações

Publicada em

Introduction of JUnit5 and TestContainers for Unit Testing

Publicada em: Software
  • Seja o primeiro a comentar

JUnit5 and TestContainers

  1. 1. JUnit5 & TestContainers Catalog tribe demo debop@coupang.com
  2. 2. Agenda • JUnit 5 • TestContainers
  3. 3. JUnit 5
  4. 4. JUnit 5 • JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage • Current version = 5.3.2 • No public modifier needed • Require Java 8 or higher • Support in IntelliJ IDEA and Eclipse IDE
  5. 5. Setup testImplementation “org.junit.jupiter:junit-jupiter-api:$jupiter_version” testRuntimeOnly “org.junit.jupiter:junit-jupiter-engine:$jupiter_version”
  6. 6. Example import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @BeforeEach fun setup() { facts.clear() } @Test fun `facts must have unique name`() { facts.put("foo", 1) facts.put("foo", 2) facts.size shouldEqual 1 assertEquals(2, facts["foo"] !!) }
  7. 7. Annotations JUnit 4 JUnit 5 @BeforeClass / @AfterClass @BeforeAll / @AfterAll @Before / @After @BeforeEach/@AfterEach @Test @Test @Test(expected=…) Assertions.assertThrows(…) { … } @Ignore @Disabled @Nested @DisplayName
  8. 8. @DisplayName("FactMap operation test") class FactMapTest { @Nested @DisplayName("When same key") inner class MutableFactMap { @Test @DisplayName("When insert duplicated key, update old value") fun `put same item`() { val o1 = facts.put("foo", 1) val o2 = facts.put("foo", 2) assertNull(o1) assertEquals(1, o2) } } @Test fun `facts must have unique name`() { // … } }
  9. 9. @Nested
  10. 10. Assertions • org.junit.jupiter.api.Assertions • Use of lambdas for lazy evaluation of messages • Grouping of assertions • New way to handle exceptions
  11. 11. Assertions @Test fun `put same item`() { val o1 = facts.put("foo", 1) val o2 = facts.put("foo", 2) assertNull(o1) { "o1 should be null" } assertEquals(1, o2) { "o2 should be 1" } }
  12. 12. Assertions assertAll( Executable { assertNull(o1) { "o1 should be null" } }, Executable { assertEquals(1, o2) { "o2 should be 1" } } ) assertThrows(IllegalArgumentException::class.java) { facts.put("", 1) } assertTimeout(Duration.ofMillis(200)) { Thread.sleep(150) } assertTimeoutPreemptively(Duration.ofMillis(200)) { Thread.sleep(150) }
  13. 13. Assumptions If assumption fails -> test is skipped @Test fun `already exists`() { // facts is empty -> assumeFalse is failed -> skip assertEquals assumeFalse { facts.isEmpty() } // Skip tests assertEquals(2, 1) }
  14. 14. Dynamic Tests @TestFactory fun testRules(): Stream<DynamicTest> { return IntStream.range(1, 3) .mapToObj { it -> dynamicTest("test for input=$it") { assertEquals(it * 2, it + it) } } } https://www.baeldung.com/junit5-dynamic-tests
  15. 15. Conditional Test Execution • ExecutionCondition as Extension API • DisabledCondition is simplest example with @Disabled annotation
  16. 16. Conditional Test Execution • @EnabledOnOS(…) • @EnabledOnJre(…) • @EnabledIfSystemProperty(named=“”, matches=“”) • @EnabledIfEnvironmentalVariable(named=“”, matches=“”) • @EnabledIf(“”) - Support for script, EXPERIMENTAL
  17. 17. Parameterized Tests • Experimental feature • Need “junit-jupiter-params” • Resources • JUnit 5 Parameterized Tests: Using Different Input • JUnit 5 ­ Parameterized Tests
  18. 18. Parameterized Tests @ParameterizedTest @ValueSource(strings = ["java8", "java9", "java10"]) fun `parameterized test`(param:String) { param shouldContain "java" }
  19. 19. Parameterized Tests @ParameterizedTest(name = "[{index}]=> {arguments}") @ValueSource(strings = ["java8", "java9", "java10"]) fun `parameterized test`(param: String) { param shouldContain "java" }
  20. 20. Parameterized Tests • @ValueSource for String, Int, Long, Double) • @EnumSource(…) • @MethodSource(“methodName”)
 method must return Stream, Iterator or Iterable • @CsvSource({“foo, bar”, “foo2, bar2”}) • @CsvFileSource(resources=…) • @ArgumentSource(MyArgProvider.class)
  21. 21. Parallel Test Execution • Resources • JUnit 5 Parallel Execution • JUnit 5 Parallel Test Execution
  22. 22. Parallel Test Execution junit.jupiter.testinstance.lifecycle.default = per_class junit.jupiter.execution.parallel.enabled = true junit.jupiter.execution.parallel.mode.default = concurrent #junit.jupiter.execution.parallel.config.strategy = dynamic #junit.jupiter.execution.parallel.config.dynamic.factor = 1 #junit.jupiter.execution.parallel.config.strategy = fixed #junit.jupiter.execution.parallel.config.fixed.parallelism = 4 #junit.jupiter.execution.parallel.config.strategy = custom #junit.jupiter.execution.parallel.config.custom.class = ...
  23. 23. Parallel Test Execution • Synchronization for shared resources • @Execution(CONCURRENT) • @Execution(SAME_THREAD) • ResourceLock(value=…, mode=…) • Value
 custom|SYSTEM_PROPERTIES|SYSTEM_OUT|SYSTEM_ERR • Mode
 READ | READ_WRITE • JUnit 5 Synchronization
  24. 24. What else? • @Tag and filtering in build script • @RepeatedTest with dynamic placeholder for @DisplayName • @TestTemplate/ TestTemplateInvocationContextProvider • Extension API, extensions registered via @ExtendWith • Custom Extensions in kotlinx-junit-jupiter
  25. 25. TestContainers
  26. 26. Introduction • Java library to launch Docker containers during Tests • Integration tests against the data access layer • Integration tests with external dependencies
 e.g. message broker, database, cache … • UI Tests with containerized, Selenium compatible, Web browsers
  27. 27. Introduction • Current version : 1.10.3 • Requires Docker installation • Requires Java 8 or higher • Compatible with JUnit 4 / 5
  28. 28. Usecases • Data access layer integration tests: use a containerized instance of a MySQL, PostgreSQL or Oracle database to test your data access layer code for complete compatibility, but without requiring complex setup on developers' machines and safe in the knowledge that your tests will always start with a known DB state.Any other database type that can be containerized can also be used. • Application integration tests: for running your application in a short-lived test mode with dependencies, such as databases, message queues or web servers. • UI/Acceptance tests: use containerized web browsers, compatible with Selenium, for conducting automated UI tests. Each test can get a fresh instance of the browser, with no browser state, plugin variations or automated browser upgrades to worry about.And you get a video recording of each test session, or just each session where tests failed.
  29. 29. JUnit 4 @ClassRule public static GenericContainer mysql = new MySQLContainer().withExposedPorts(3306); @Test public void getExposedPorts() { List<Integer> ports = mysql.getExposedPorts(); assertThat(ports).contains(3306); }
  30. 30. JUnit 4 + Kotlin static { INSTANCE = createPostgreSQLContainer(); HOST = INSTANCE.getContainerIpAddress(); PORT = INSTANCE.getMappedPort(PostgreSQLContainer.POSTGRESQL_PORT); JDBC_URL = INSTANCE.getJdbcUrl(); } public static PostgreSQLContainer createPostgreSQLContainer() { PostgreSQLContainer container = new PostgreSQLContainer(); container.withLogConsumer(new Slf4jLogConsumer(log)); container.withDatabaseName("test"); container.start(); return container; }
  31. 31. JUnit 5 static GenericContainer mysql = new MySQLContainer().withExposedPorts(3306); @BeforeAll static void setup() { mysql.start(); } @AfterAll static void tearDown() { mysql.stop(); } @Test void getExposedPorts() { List<Integer> ports = mysql.getExposedPorts(); assertThat(ports).contains(3306); }
  32. 32. Generic Container • Offers flexible support for any container image as test dependency • Reference public docker images • Internal dockerized services
  33. 33. Generic Container • withExposedPorts(…) • withEnv(…) • withLabel(…) • getContainerIpAddress() • getMappedPort(…)
  34. 34. Generic Container - Kotlin object RedisContainer : KLogging() { // For Kotlin language spec class KGenericContainer(imageName: String) : GenericContainer<KGenericContainer>(imageName) val instance by lazy { startRedisContainer() } private fun startRedisContainer() = KGenericContainer("redis:4.0.11").apply { withExposedPorts(6379) setWaitStrategy(HostPortWaitStrategy()) withLogConsumer(Slf4jLogConsumer(log)) start() } val host: String by lazy { instance.containerIpAddress } val port: Int by lazy { instance.getMappedPort(6379) } val url: String by lazy { "redis://$host:$port" } }
  35. 35. Specialized Container • Create images from Dockerfile • withFileFromString(…) • withFileFromClasspath(…) • Use Dockerfile DSL to define Dockerfiles in code
  36. 36. Specialized Container • Use database container to test database specific features • No local setup or VM • 100% database compatibility instead of H2 • MySQL • PostgreSQL • Oracle XE
  37. 37. Future • TestContainers 2.x • API cleanup • Decoupling from JUnit 4 to support other framework directly
  38. 38. Alternatives • TestNG • Other JVM languages • Groovy, Spock, Testcontainers-Spock • Kotlin, Testcontainers (with workaround)
  39. 39. Resources • JUnit 5 • JUnit 5 Basic • JUnit 5 : Next step in automated testing • How to perform a productivee testing using JUnit 5 on Kotlin • Test Containers • Test containers Official site • Docker Test Containers in Java Test • TestContainers and Spring Boot • Don’t use In-Memory Database (H2, Congo) for Tests
  40. 40. Q&A
  41. 41. Thank you!

×