Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Modern Testing Tools for Java Developers

Modern Testing Tools for Java Developers

Elias Nogueira

May 30, 2023
Tweet

More Decks by Elias Nogueira

Other Decks in Technology

Transcript

  1. MODERN TESTING TOOLS FOR
    JAVA DEVELOPERS

    View Slide

  2. ELIAS NOGUEIRA
    Principal Software Engineer
    Brazil
    Oracle Ace
    Java Magazine NL Editor

    View Slide

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

    View Slide

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

    View Slide

  5. 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

    View Slide

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

    View Slide

  7. 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);
    }

    View Slide

  8. 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"));
    });
    }

    View Slide

  9. 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
    }

    View Slide

  10. 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

    View Slide

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

    View Slide

  12. 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(""));
    }
    }

    View Slide

  13. 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);
    }

    View Slide

  14. 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

    View Slide

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

    View Slide

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

    View Slide

  17. 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

    View Slide

  18. 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);
    }

    View Slide

  19. 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();
    }

    View Slide

  20. 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

    View Slide

  21. 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.

    View Slide

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

    View Slide

  23. 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))));
    }
    }

    View Slide

  24. 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

    View Slide

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

    View Slide

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

    View Slide

  27. 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

    View Slide

  28. 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

    View Slide

  29. MORE
    MODER TESTING TOOLS

    View Slide

  30. TestContainers
    ArchUnit
    DataFaker
    Playwright

    View Slide

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

    View Slide