Slide 1

Slide 1 text

MODERN TESTING TOOLS FOR JAVA DEVELOPERS

Slide 2

Slide 2 text

ELIAS NOGUEIRA Senior Principal Software Engineer Brazil Oracle Ace Java Magazine NL Editor Java Champion

Slide 3

Slide 3 text

AssertJ REST-Assured Awaitility WireMock 1⃣ 2⃣ 3⃣ 4⃣ PiTest 5⃣ agenda

Slide 4

Slide 4 text

AssertJ Assertion library https://github.com/assertj/assertj 2.4k ⭐ on GitHub

Slide 5

Slide 5 text

AssertJ Assertion library that provides extensive assertion methods, adding readability to the tests. In a fluent way, you can assert: • Common types: Optional, Predicates, Streams, Iterable … • Primitive types • Java 8 Temporal Types • Atomic Types

Slide 6

Slide 6 text

AssertJ The cool features • Recursive Comparison by Field • Soft Assertions • Custom Assertions

Slide 7

Slide 7 text

AssertJ The cool features: Recursive Comparison by Field @Test void recursiveComparisonTest() { var simulation1 = Simulation.builder().name("John").cpf("9582728395") .email("[email protected]").amount(new BigDecimal("500")).installments(1) .insurance(false).build(); var simulation2 = SimulationDto.builder().name("John").cpf("9582728395") .email("[email protected]").amount(new BigDecimal("500")).installments(1) .insurance(false).build(); // ✅ won't fail because the assertion is done field by field assertThat(simulation1).usingRecursiveComparison().isEqualTo(simulation2); // ❌ will fail because equals compare references assertThat(simulation1).isEqualTo(simulation2); }

Slide 8

Slide 8 text

AssertJ The cool features: Soft Assertions @Test void softAssertionTest() { var simulation = Simulation.builder().name("John").cpf("9582728395") .email("[email protected]").amount(new BigDecimal("500")) .installments(1).insurance(false).build(); SoftAssertions.assertSoftly(softly -> { softly.assertThat(simulation.getName()).isEqualTo("John"); softly.assertThat(simulation.getInsurance()).isTrue(); softly.assertThat(simulation.getAmount()).isGreaterThan(new BigDecimal("2.00")); }); }

Slide 9

Slide 9 text

AssertJ The cool features: Custom Assertions @Test void simulationValidationErrorNormalAssertion() { var simulation = Simulation.builder().name("John").cpf("9582728395") .email("[email protected]").amount(new BigDecimal("500")) .installments(1).insurance(false).build(); SimulationAssert.assertThat(simulation).hasValidInstallments(); SimulationAssert.assertThat(simulation).hasValidAmount(); // never gets here }

Slide 10

Slide 10 text

Talk is cheap. Show me the code. Linus Torvalds

Slide 11

Slide 11 text

Examples Test • Test class using both Custom and Custom Soft Assertion Custom Assertion • Custom assertion methods based on a context (Simulation) Scalable one • Custom Soft Assertion using the the Custom Assertion

Slide 12

Slide 12 text

REST-Assured DSL for API Testing https://rest-assured.io 6.4k ⭐ on GitHub

Slide 13

Slide 13 text

REST-Assured Most powerful API Testing library for Java, which is a DSL to simplify testing of REST based services based on the given-when-then keywords (but it’s not BDD). class RestAssuredExampleTest { @Test void welcome() { given() .param("name", "Elias"). when() .post("/register"). then() .statusCode(200) .body("message", is("")); } }

Slide 14

Slide 14 text

REST-Assured You can have multiple tests like this, but it won’t scale! @Test @DisplayName("Should validate all existing simulations") void getAllExistingSimulations() { var existingSimulations = SimulationDataFactory.allExistingSimulations(); var simulationsRequested = when(). get("/simulations"). then(). statusCode(SC_OK). extract(). as(Simulation[].class); Assertions.assertThat(existingSimulations).contains(simulationsRequested); }

Slide 15

Slide 15 text

REST-Assured We can fast create the architecture and decrease the test maintenance by: • Using the openapi-generator to create the client based on REST- Assured • Create a service layer in the test project

Slide 16

Slide 16 text

Talk is cheap. Show me the code. Linus Torvalds

Slide 17

Slide 17 text

Examples Basic one (don’t scale) • Raw usage Intermediate one • Request and Response Specification Scalable one • Service Abstraction + OpenAPI Generator

Slide 18

Slide 18 text

Awaitility DSL that allows you to express expectations of an asynchronous system http://www.awaitility.org 3.4k ⭐ on GitHub

Slide 19

Slide 19 text

Awaitility DLS to solve the problem, during the test, to wait for an asynchronous system (RabbitMQ, Apache Kafka, Redis, and others) to complete the request. It has the ability to: • create conditions to wait • check fields • support Atomic structures • ignoring and checking exceptions • integrate with AssertJ

Slide 20

Slide 20 text

Awaitility The test will fail because the event will take some time to complete and there’s no indication of waiting for something to happen. @Test void checkEventWithRestriction() { String cpf = "26276298085"; checkForRestriction(cpf); given().pathParam("cpf", cpf).when().get("/events/{cpf}").then().statusCode(SC_OK); }

Slide 21

Slide 21 text

Awaitility You can use the Repository object, or a SQL query, to check for the data, and then continue the code execution. @Test void checkEventWithRestriction_Success() { String cpf = "55856777050"; checkForRestriction(cpf); await().atMost(10, SECONDS).until(eventIsFound(cpf)); given().pathParam("cpf", cpf).when().get("/events/{cpf}").then().statusCode(SC_OK); } // 👇 the Callable will do the trick public Callable eventIsFound(String cpf) { return () -> eventRepository.findByCpf(cpf).isPresent(); }

Slide 22

Slide 22 text

Talk is cheap. Show me the code. Linus Torvalds

Slide 23

Slide 23 text

Examples How to see, locally, the delay in the database insertion • Use the event-source branch • Start the application • Login to H2 (sa/password) • Run a query into the EVENT_SOURCE table • Make a POST request (you can use the Swagger UI) • Run a query into the EVENT_SOURCE table • The insertion will occur only 7 seconds after the request

Slide 24

Slide 24 text

Examples Running the example • Test class: EventTest • Run the checkEventWithRestriction_Fail() and see it failing because of the 7 second delay • Run the checkEventWithRestriction_Success() and see the test green Disclaimer There’s no asynchronous system here. A delay of 7 seconds was added in the EventService that will be called by the RestrictionController methods and SimulationsController for the POST and DELETE methods.

Slide 25

Slide 25 text

WireMock Tool to build mock APIs https://wiremock.org 5.4k ⭐ on GitHub

Slide 26

Slide 26 text

WireMock One of the most popular mock open-source tools for API mock testing. You probably know it by the isolated usage in your unit tests, like this: @WireMockTest public class WiremockTest { @Test void testUsingWiremock() { String response = """ { "error": "The cpf 987654321 as a restriction" } """; stubFor((get(urlEqualTo("/restriction")) .withQueryParam("cpf", equalTo("987654321")) .willReturn( aResponse().withResponseBody(response)))); } }

Slide 27

Slide 27 text

WireMock Instead of mocking something you need, that will be duplicated across the teams, you can implement a Service Virtualization approach using WireMock to share the same mock across multiple teams. Team 1 Team 2 Team N Service Virtualization Standalone local container Deploy in a k8s infra

Slide 28

Slide 28 text

Talk is cheap. Show me the code. Linus Torvalds

Slide 29

Slide 29 text

Example General explanation • Blog post Projects • Container • API Service

Slide 30

Slide 30 text

pitest (PIT) Mutation Testing library https://pitest.org/ 1.5k ⭐ on GitHub

Slide 31

Slide 31 text

pitest What is Mutation Testing? Is the approach to add faults in your code based on a series of mutants (specific code changes) showing you if your code has lived or killed mutations. • Lived mutations: indicates you have something to work on :) • Killed mutations: means that the change applied is covered

Slide 32

Slide 32 text

pitest Why Mutation Testing? Because the normal test coverage approach measures only the executed code. It adds the support of fault detections which are conditions we haven't added in the tests, mostly corner cases. There're a lot of mutators, and the most common ones are: • Conditional Boundary • Increment • Math

Slide 33

Slide 33 text

Talk is cheap. Show me the code. Linus Torvalds

Slide 34

Slide 34 text

MORE MODER TESTING TOOLS

Slide 35

Slide 35 text

TestContainers ArchUnit DataFaker Playwright

Slide 36

Slide 36 text

THANK YOU! @eliasnogueira https://github.com/eliasnogueira/public-speaking