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

More Decks by Elias Nogueira

Other Decks in Technology


  1. 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
  2. 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); }
  3. 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")); }); }
  4. 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 }
  5. 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
  6. 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("")); } }
  7. 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); }
  8. 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
  9. Examples Basic one (don’t scale) • Raw usage Intermediate one

    • Request and Response Specification Scalable one • Service Abstraction + OpenAPI Generator
  10. Awaitility DSL that allows you to express expectations of an

    asynchronous system http://www.awaitility.org 3.4k ⭐ on GitHub
  11. 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
  12. 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); }
  13. 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<Boolean> eventIsFound(String cpf) { return () -> eventRepository.findByCpf(cpf).isPresent(); }
  14. 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
  15. 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.
  16. 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)))); } }
  17. 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
  18. 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
  19. 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