advertisement

Testing For Unicorns

60 %
40 %
advertisement
Information about Testing For Unicorns

Published on June 19, 2017

Author: asotobu

Source: slideshare.net

advertisement

1. Testing For Unicorns From Unit to Deployment Tests Alex Soto
 @alexsotob

2. @alexsotob2 Alex Soto Red Hat Engineer www.lordofthejars.com @alexsotob Who Am I?

3. @alexsotob3 Questions

4. @alexsotob4 Click to add subtitle ASSERTIONS

5. @alexsotob JUnit Test @Test public void should_find_composer_by_name() { // Given: Composers composers = new Composers(); // When: final Composer mozart = composers.findComposerByName("Wolfgang Amadeus Mozart"); // Then: assertEquals("Wolfgang Amadeus Mozart", mozart.getName()); assertEquals(Era.CLASSICAL, mozart.getEra()); assertEquals(LocalDate.of(1756, 1, 27), mozart.getBirthdate()); assertEquals(LocalDate.of(1791, 12, 5), mozart.getDied()); } 5 Readable name BDD style AssertEquals Order Depends On Equals

6. @alexsotob6 AssertJ

7. @alexsotob AssertJ Test import static org.assertj.core.api.Assertions.assertThat; @Test public void should_find_composer_by_name() { // Given: Composers composers = new Composers(); // When: final Composer mozart = composers.findComposerByName("Wolfgang Amadeus Mozart”); // Then: assertThat(mozart.getName()).isEqualTo("Wolfgang Amadeus Mozart”); assertThat(mozart).returns("Wolfgang Amadeus Mozart", Composer::getName); assertThat(mozart.getBirthdate()).isEqualTo(LocalDate.of(1756, 1, 27)); assertThat(mozart).isEqualToComparingFieldByField(expectedMozart); assertThat(mozart).isEqualToIgnoringNullFields(expectedMozart); } 7 Static Import Readable Assertions Not Depending on Equals

8. @alexsotob AssertJ Test Collections @Test public void should_find_operas_by_composer_name() { // Given: Composers composers = new Composers(); // When: final List<Opera> operas = composers.findOperasByComposerName("Wolfgang Amadeus Mozart"); // Then: assertThat(operas) .hasSize(2) .extracting(Opera::getName) .containsExactlyInAnyOrder("Die Zauberflöte", "Don Giovanni”); } 8 Train Call (IDE support) Create List with getName Result Methods for String

9. @alexsotob AssertJ Soft Assertions @Test public void should_find_composer_by_name_soft_assertions() { // Given: Composers composers = new Composers(); // When: final Composer mozart = composers.findComposerByName("Wolfgang Amadeus Mozart"); // Then: SoftAssertions.assertSoftly(softly -> { softly.assertThat(mozart.getName()).isEqualTo("Wolfgang Amadeus Mozart"); softly.assertThat(mozart.getEra()).isEqualTo(Era.CLASSICAL); softly.assertThat(mozart.getBirthdate()).isEqualTo(LocalDate.of(1756, 1, 27)); softly.assertThat(mozart.getDied()).isEqualTo(LocalDate.of(1791, 12, 5)); }); } 9 Java 8 Lambda All assertions in Block

10. @alexsotob10 Things go Wrong

11. @alexsotob Try/Catch @Test public void should_throw_exception_if_composer_not_found() { // Given: Composers composers = new Composers(); // When: try { final Composer salieri = composers.findComposerByName("Antonio Salieri"); fail(); } catch (IllegalArgumentException e) { // Then: assertEquals("Composer Antonio Salieri is not found", e.getMessage()); } } 11 Fails if Success

12. @alexsotob JUnit @Test(expected = IllegalArgumentException.class) public void should_throw_exception_if_composer_not_found_version_2() { // Given: Composers composers = new Composers(); // When: final Composer salieri = composers.findComposerByName("Antonio Salieri"); } 12 Special Attribute

13. @alexsotob AssertJ Exceptions @Test public void should_throw_exception_if_composer_not_found_version_3() { // Given: Composers composers = new Composers(); // When: Throwable thrown = catchThrowable(() -> composers.findComposerByName("Antonio Salieri")); // Then: assertThat(thrown).isInstanceOf(IllegalArgumentException.class) .withFailMessage("Composer Antonio Salieri is not found”); } 13 Catch Exception of Lambda Assertion Methods for Exceptions

14. @alexsotob IDE Friendly Ctrl + Space works Assertions Generation ComposerAssert.assertThat(mozart).hasName(“Mozart”).hasBirthdate(LocalDate.of(..); Out-of-the-Box Assertions Guava, Joda, DB, Neo4j and Swing Benefits of AssertJ 14

15. @alexsotob15 Don’t Sleep, Just Wait

16. @alexsotob Asynchronous Call @Test public void should_play_operas() throws InterruptedException { // Given: final Opera nozzeDiFigaro = ...; Gramophone gramophone = new Gramophone(); // When: gramophone.play(nozzeDiFigaro); // Then: TimeUnit.SECONDS.sleep(3); assertThat(gramophone.getCurrentOpera()).isEqualTo(nozzeDiFigaro); } 16 Asynchronous Call Slowest Machine Time

17. @alexsotob17

18. @alexsotob Awaitility Example @Test public void should_play_operas_version_2() { // Given: final Opera nozzeDiFigaro = Composers.OperaFactory .createOpera("Le Nozze di Figaro") .language(Language.ITALIAN).librettist("Lorenzo Da Ponte") .roles("Count Almaviva", "Countess Rosina", "Susanna", "Figaro") .build(); Gramophone gramophone = new Gramophone(); // When: gramophone.play(nozzeDiFigaro); // Then: await().atMost(5, TimeUnit.SECONDS).until(gramophone::isPlaying); assertThat(gramophone.getCurrentOpera()).isEqualTo(nozzeDiFigaro); } 18 Asynchronous Call Polls until True or Timeout

19. @alexsotob Deadlock Detection Different Pollings Fixed, Fibonacci, Iterative, Custom Simple Library No dependencies, No magic Benefits of Awaitility 19

20. @alexsotob20 REST API

21. @alexsotob GET /Ludwig+van+Beethoven { "name": "Ludwig van Beethoven", "era": "ROMANTIC", "birthdate": {}, "died": {}, "operas": [ { "name": "Fidelio", "librettist": "Georg Friedrich Treitschke", "language": "GERMAN", "roles": ["Rocco", "Leonore", "Florestan"] } ] } 21 Simple Objects Array of Objects

22. @alexsotob HttpClient Example @Test public void should_find_composer() throws IOException, URISyntaxException { // Given: URIBuilder uriBuilder = new URIBuilder("http://localhost:8080/"); uriBuilder.setPath("Ludwig van Beethoven"); // When: final Content bodyContent = Request.Get(uriBuilder.build()) .execute().returnContent(); String body = bodyContent.asString(); // Then: assertThat(body).contains(""name":"Ludwig van Beethoven"") .contains(""librettist":"Georg Friedrich Treitschke""); } 22 Prepare Connection Do connection Get content Manipulate String

23. @alexsotob WebDriver Example // Given: WebDriver browser = new FirefoxDriver(); URIBuilder uriBuilder = new URIBuilder("http://localhost:8080/"); uriBuilder.setPath("Ludwig van Beethoven"); // When: browser.navigate().to(uriBuilder.build()); // Then: assertThat(browser.getPageSource()).contains(""name":"Ludwig van Beethoven""); 23

24. @alexsotob24

25. @alexsotob REST-assured Example @Test public void should_find_composer() { given() .when() .get("{composer}", "Ludwig van Beethoven") .then() .assertThat() .body("name", is("Ludwig van Beethoven")) .body("operas.size()", is(1)) .body("operas.name", hasItems("Fidelio")); } 25 GET Http Method with Placeholders GPath Expression

26. @alexsotob REST-assured Request Specification .get("http://example.com/{composer}", "Ludwig van Beethoven") RequestSpecBuilder builder = new RequestSpecBuilder(); builder.setBaseUri("http://example.com"); given().spec(builder.build())... 26 Use domain directly Use Spec Builder Reuse Everywhere

27. @alexsotob REST-assured Auth given().auth().oauth2(accessToken).when()... given().auth().form("John", "Doe", springSecurity().withCsrfFieldName("_csrf")).when()... given().auth().basic("username", "password").when()... 27

28. @alexsotob Custom Parsers Not just JSON and XML SSL Support .relaxedHTTPSValidation() Filters Input/Output modification JSON Schema Validation Content not Important More Features of Rest-Assured 28

29. @alexsotob29 (Micro) Services Dependencies

30. @alexsotob Network Down Service Down Limits on API Component Not in Control Micro-Services Dependencies Hell Problems with Services 30

31. @alexsotob Mock Http Component Stub/Fake Http Server Service Virtualization Possible Solutions 31

32. @alexsotob32 Service Virtualization

33. @alexsotob Service Virtualization Capture Mode 33 Service A External Network Service B Scripts Proxy

34. @alexsotob Service Virtualization Simulate Mode 34 Service A External Network Service B Scripts Proxy

35. @alexsotob35

36. @alexsotob Hoverfly Example @ClassRule public static HoverflyRule hoverflyRule = HoverflyRule.inCaptureOrSimulationMode("getcomposers.json"); @Test public void should_get_composers_from_composers_microservice() { // Given: ComposersGateway composersGateway = new ComposersGateway("operas.com", 8081); // When: Composer composer = composersGateway.getComposer("Ludwig van Beethoven"); // Then: assertThat(composer.getName()).isEqualTo("Ludwig van Beethoven"); } 36 Start Hoverfly Use Real Host Get Data from Real { "data" : { "pairs" : [{ "request" : { "path" : "/Ludwig van Beethoven", "method" : "GET", "destination" : “operas.com:8081", ... } "response" : { "status" : 200, "body" : "{"name":"Ludwig van Beethoven",}", "encodedBody" : false, "headers" : { "Connection" : [ "keep-alive" ], ... } } } } Get Data from Proxy

37. @alexsotob37 Persistence Tests

38. @alexsotob First attempt @Test public void should_find_composer_by_name() { // Given: clearDatabase(jdbcUri); insertComposersData(jdbcUri); ComposersRepository composersRepository = new ComposersRepository(); // When: Composer mozart = composersRepository.findComposerByName("Wolfgang Amadeus Mozart"); // Then: assertThat(mozart).returns("Wolfgang Amadeus Mozart", Composer::getName); } 38 Prepare Database Execute Query

39. @alexsotob39 APE

40. @alexsotob APE SQL example @Rule public ArquillianPersistenceRule arquillianPersistenceRule = new ArquillianPersistenceRule(); @DbUnit @ArquillianResource RdbmsPopulator rdbmsPopulator; @Before public void populateData() { // Given: rdbmsPopulator.forUri(jdbcUri).withDriver(Driver.class).withUsername("sa") .withPassword("").usingDataSet("composers.yml") .execute(); } 40 APE JUnit Rule (not necessary with Arquillian Runner) Set DBUnit usage Configure Connection and Dataset Populate composers: - id: 1 name: Wolfgang Amadeus Mozart birthdate: 27/1/1756 died: 5/12/1791

41. @alexsotob APE SQL example @After public void clean_database() { // Given: rdbmsPopulator.forUri(jdbcUri).withDriver(Driver.class).withUsername("sa") .withPassword("").usingDataSet("composers.yml") .clean(); } 41 Clean After Test Clean Database

42. @alexsotob Boilerplate Code Programmatic/Declarative @UsingDataSet/@ShouldMatchDataSet SQL Support DBUnit and Flyway REST API Support Postman Collections NoSQL Support MongoDB, Couchbase, CouchDB, Vault, Redis, Infinispan Benefits of Arquillian APE 42

43. @alexsotob43 Containers Are Burning

44. @alexsotob Testing Containers docker build -t myorg/myservice:1.0.0 . docker run --rm -ti -p 8080:8080 myorg/myservice:1.0.0 docker-compose up mvn clean test docker-compose stop 44 Docker Run Docker Compose Run Run tests Stop Docker Containers

45. @alexsotob45 CUBE

46. @alexsotob Arquillian Cube Example @RunWith(Arquillian.class) public class HelloWorldTest { @ArquillianResource @DockerUrl(containerName = "helloworld", exposedPort = 8080) RequestSpecBuilder requestSpecBuilder; @Test public void should_receive_ok_message() { RestAssured .given() .spec(requestSpecBuilder.build()) .when() .get() .then() .assertThat().body("status", equalTo("OK")); } } 46 Arquillian Runner REST-Assured Integration Environment Resolver Normal REST-Assured Call helloworld: image: jonmorehouse/ping-pong ports: - "8080:8080" src/test/docker/docker-compose.yml

47. @alexsotob Arquillian Cube DSL @RunWith(SpringRunner.class) @SpringBootTest(classes = PingPongController.class, webEnvironment = RANDOM_PORT) @ContextConfiguration(initializers = PingPongSpringBootTest.Initializer.class) public class PingPongSpringBootTest { @ClassRule public static ContainerDslRule redis = new ContainerDslRule("redis:3.2.6") .withPortBinding(6379); @Autowired TestRestTemplate restTemplate; @Test public void should_get_data_from_redis() { } 47 Spring Boot Test Custom Initializer Container Definition public static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> { @Override public void initialize(ConfigurableApplicationContext configurableApplicationContext) { EnvironmentTestUtils.addEnvironment("testcontainers", configurableApplicationContext.getEnvironment(), "spring.redis.host=" + redis.getIpAddress(), "spring.redis.port=" + redis.getBindPort(6379) ); Sets Container Environment

48. @alexsotob Arquillian Cube K8S @RunWith(Arquillian.class) public class HelloWorldTest { @ArquillianResource KubernetesClient client; @Named(“hello-world-service") @PortForward @ArquillianResource URL url; @Test public void testRunningPodStaysUp() throws Exception { assertThat(client).deployments().pods().isPodReadyForPeriod(); } } 48 Kubernetes Client URL to Access Service AssertJ Custom Assertions

49. @alexsotob49 Let’s Wind Down

50. @alexsotob From Low to High Level Unit, Component, Integration, Deployment Not Just for Micro-Services Same can be reused for monolithic Improves Readability Tests are meant to be read Conclusions 50

51. @alexsotob51 Keep Calm And Write Tests

Add a comment