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

Managing Test Data

Managing Test Data

Have you ever tried to troubleshoot an issue taking a looking at the log files? I bet you did! And it turns out the issue is related to data usage because, you know, users will use real data! Developers won't! A good thing about your recent troubleshooting is that you can understand the data. It's not a bunch of numbers or UUIDs in the name field. How about your development environment? Probably you use either hard-coded data or random strings.

This talk will show you how to manage testing data in two different ways.
The first way is the creation of a Test Data Factory approach, where we will use the Factory Pattern to easily create any objects we want with understandable and reliable data. This approach will make you in control of your own data, so you can find any bug in the early stages of the SDLC and the DataFaker library will help us to generate good random data in every execution.

In a second way, we will apply the Data-Driven Testing approach, where the same requirement can be applied to different sets of data, generating different results. This approach can reduce the amount of code and the execution time. With the support of JUnit 5 you will learn how to do it in different ways: value source, internal method source, external method source, argument provider, and CVS source.

You will learn how to write Data-Driven tests using JUnit 5 in a SpringBoot application in the integration layer, using the Test Data Factory approach to create necessary and reliable data.

Elias Nogueira

March 22, 2023
Tweet

More Decks by Elias Nogueira

Other Decks in Technology

Transcript

  1. MANAGING TEST DATA
    @eliasnogueira

    View Slide

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

    View Slide

  3. THE PROBLEM WE
    WANT TO SOLVE

    View Slide

  4. hard troubleshooting
    data without meaning is hard to understand
    [INFO ] 2023-03-05 18:03:54.081 [main] Person - model1 - Person[
    name=etSfgOGRQA,
    surname=USwRiZSZnKdYAlYBNYJVcDFRgmSLfO,
    [email protected],
    age=1,
    address=XmcWoqivEzSoUwEksGNjlzrjUmWKKCIDOTsDnPAlHPQMJaWYzt]
    [INFO ] 2023-03-05 18:03:54.090 [main] Person - model1 - Person[
    name=rtxhUtqzhV,
    surname=yruPCovuOMXrXUKqZnkxgrDIsdUWMY,
    [email protected],
    age=60,
    address=tFlnvWupyHnnHkAzuHAFzmRbrFUjwViqHSUEDjqCWfQdjKhBFM]

    View Slide

  5. hard troubleshooting
    data without meaning is hard to understand
    [INFO ] 2023-03-05 18:03:54.081 [main] Person - model1 - Person[
    name=etSfgOGRQA,
    surname=USwRiZSZnKdYAlYBNYJVcDFRgmSLfO,
    [email protected],
    age=1,
    address=XmcWoqivEzSoUwEksGNjlzrjUmWKKCIDOTsDnPAlHPQMJaWYzt]

    View Slide

  6. not prioritized data requirements
    data must match any rule or restriction
    [INFO ] 2023-03-05 18:03:54.081 [main] Person - model1 - Person[
    name=etSfgOGRQA,
    surname=USwRiZSZnKdYAlYBNYJVcDFRgmSLfO,
    [email protected],
    age=1,
    address=XmcWoqivEzSoUwEksGNjlzrjUmWKKCIDOTsDnPAlHPQMJaWYzt]
    probably will make a test fail

    View Slide

  7. no patterns for data generation
    we must avoid code duplication
    private static Person personToCreate() {
    Person person = new Person(
    "Test user",
    "Surname",
    "[email protected]",
    18,
    "User address, 50 – Brühl"
    );
    return person;
    }
    multiple methods to generate
    the same data can (wrongly) exist

    View Slide

  8. hard troubleshooting
    not prioritized data requirements
    no patterns for data generation
    - possibly fixed/data repetition

    View Slide

  9. HOW TO SOLVE THE
    PROBLEM?

    View Slide

  10. define a way to generate data
    use a specialized tool
    understand the data requirements
    know the testing library support for data
    1⃣
    2⃣
    3⃣
    4⃣

    View Slide

  11. DEFINE A WAY TO
    GENERATE DATA

    View Slide

  12. There are some approaches to generate data
    Combining Object Mother and Fluent Builder for the
    Ultimate Test Data Factory
    Test Data Builders and Object Mother: another look
    Writing Clean Tests – New Considered Harmful
    Test Data Builders: an alternative to the Object Mother
    pattern
    define a way to generate data

    View Slide

  13. common approaches
    builder pattern, in
    the Model objects
    factory pattern to
    generate the data
    tool for dynamic
    data generation
    test data factory

    View Slide

  14. model object
    @Data
    @Builder
    @EqualsAndHashCode
    public class Simulation {
    @JsonIgnore
    private Long id;
    private String name;
    private String cpf;
    private String email;
    private BigDecimal amount;
    private Integer installments;
    private Boolean insurance;
    }

    View Slide

  15. builder pattern
    var simulation = Simulation.builder()
    .cpf("94827495037")
    .name("John Doe")
    .email("[email protected]")
    .amount(new BigDecimal("1000"))
    .installments(5)
    .insurance(true)
    .build();

    View Slide

  16. factory pattern
    public final class SimulationDataFactory {
    public static Simulation createSimulation() {
    return Simulation.builder().cpf("94827495037")
    .name("John Doe").email("[email protected]")
    .amount(new BigDecimal("1000")).installments(5)
    .insurance(true).build();
    }
    }

    View Slide

  17. USE A SPECIALIZED TOOL

    View Slide

  18. https://www.slideshare.net/JaapCoomans/fairies-fakers-and-factories-boost-your-tests-with-better-test-data

    View Slide

  19. use a specialized tool
    Tool What it does Comment
    Easy Random
    Generate random data
    based on Java beans
    For a log time not updated (3 years)
    Uses java faker for specialized data
    jFairy
    Generate data based on
    different aspects
    Uses Apache Commons library, IBAJ4J,
    and custom YAML files
    Instancio
    Creates and populates
    objects
    Uses a custom solution for data
    generation
    DataFaker
    Generate data based on
    YAML and random code
    Fork from JavaFaker with more active
    community

    View Slide

  20. datafaker
    Faker faker = new Faker(Locale.GERMAN);
    faker.name().fullName(); !// Freiherrin Amar von Lienshöft
    faker.finance().iban("DE"); !// DE37941528189220696520
    faker.internet().emailAddress(); !// [email protected]
    faker.expression("!#{date.birthday ‘yy MM dd hh:mm:ss'}"); !// 78 10 08 10:04:34

    View Slide

  21. test data factory
    public final class SimulationDataFactory {
    public static Simulation createSimulation() {
    return Simulation.builder()
    .cpf("94827495037")
    .name("John Doe")
    .email(("[email protected]")
    .amount(new BigDecimal("1000"))
    .installments(5)
    .insurance(true)
    .build();
    }
    }
    Combination of builder + factory

    View Slide

  22. test data factory
    public final class SimulationDataFactory {
    public static Simulation createSimulation() {
    return Simulation.builder()
    .cpf(faker.cpf().valid())
    .name(faker.name().fullName())
    .email(faker.internet().emailAddress())
    .amount(new BigDecimal(faker.number().numberBetween(1000, 40000)))
    .installments(faker.number().numberBetween(2, 48))
    .insurance(faker.bool().bool())
    .build();
    }
    }
    Combination of builder + factory + tool

    View Slide

  23. UNDERSTAND THE
    DATA REQUIREMENTS

    View Slide

  24. understand the data requirements
    When we have data requirements to follow…
    @Min(value = 1000, message = "Amount must be equal or greater than $ 1.000")
    @Max(value = 40000, message = "Amount must be equal or less than than $ 40.000")
    private BigDecimal amount;
    @Min(value = 2, message = "Installments must be equal or greater than 2")
    @Max(value = 48, message = "Installments must be equal or less than 48")
    private Integer installments;

    View Slide

  25. public final class SimulationDataFactory {
    public static Simulation createSimulation() {
    return Simulation.builder()
    .cpf(faker.cpf().valid())
    .name(faker.name().fullName())
    .email(faker.internet().emailAddress())
    .amount(new BigDecimal(faker.number().numberBetween(1000, 40000)))
    .installments(faker.number().numberBetween(2, 48))
    .insurance(faker.bool().bool()).build();
    }
    }
    We must generate the data based on that conditions
    understand the data requirements

    View Slide

  26. KNOW THE TESTING LIBRARY
    SUPPORT FOR DATA

    View Slide

  27. jUnit 5 data driven
    Value Source
    Internal Method Source
    External Method Source
    Argument Provider
    CSV Provider

    View Slide

  28. Value Source
    jUnit 5 data driven
    class JUnitValueSourceTest {
    private static final int VALUE = 7;
    @DisplayName("Values are greater than or equals to")
    @ParameterizedTest(name = "{0} is greater than or equals to " + VALUE)
    @ValueSource(ints = {7, 10, 12, 40})
    void valueSourceExample(int value) {
    assertThat(value).isGreaterThanOrEqualTo(VALUE);
    }
    }

    View Slide

  29. Internal Method Source
    jUnit 5 data driven
    class JUnitInternalMethodSourceTest {
    @DisplayName("All products should be cheap")
    @ParameterizedTest(name = "product ''{0}'' of amount ${1} is cheap")
    @MethodSource("cheapProducts")
    void cheapProducts(String product, BigDecimal amount) {
    final BigDecimal maximumPrice = new BigDecimal("30.0");
    assertThat(product).isNotEmpty();
    assertThat(amount).isLessThanOrEqualTo(maximumPrice);
    }
    static Stream cheapProducts() {
    return Stream.of(
    arguments("Micro SD Card 16Gb", new BigDecimal("6.09")),
    arguments("JBL GO 2", new BigDecimal("22.37")),
    arguments("iPad Air Case", new BigDecimal("14.99"))
    );
    }
    }

    View Slide

  30. jUnit 5 data driven
    class JUnitExternalMethodSourceTest {
    private final String CHEAP_PRODUCTS = "com.eliasnogueira.datadriven.JUnitExternalData#cheapProducts";
    @ParameterizedTest(name = "product ''{0}'' of amount ${1} is cheap")
    @MethodSource(value = CHEAP_PRODUCTS)
    void cheapProducts(String product, BigDecimal amount) {
    !// test goes here
    }
    public class JUnitExternalData {
    public static Stream cheapProducts() {
    return Stream.of(
    arguments("Micro SD Card 16Gb", new BigDecimal("6.09")),
    arguments("JBL GO 2", new BigDecimal("22.37")),
    arguments("iPad Air Case", new BigDecimal("14.99"))
    );
    }
    External Method Source

    View Slide

  31. Argument Provider
    jUnit 5 data driven
    class JUnitArgumentProviderTest {
    private static final String MAXIMUM_PRICE = "30.0";
    @DisplayName("Products should not exceed the maximum price")
    @ParameterizedTest(name = "product ''{0}'' of amount ${1} does not exceeds $" + MAXIMUM_PRICE)
    @ArgumentsSource(ProductsDataArgumentProvider.class)
    void cheapProducts(String product, BigDecimal amount) {
    assertThat(product).isNotEmpty();
    assertThat(amount).isLessThanOrEqualTo(new BigDecimal(MAXIMUM_PRICE));
    }
    }
    public class ProductsDataArgumentProvider implements ArgumentsProvider {
    @Override
    public Stream extends Arguments> provideArguments(ExtensionContext extensionContext) {
    return Stream.of(
    arguments("Micro SD Card 16Gb", new BigDecimal("6.09")),
    arguments("JBL GO 2", new BigDecimal("22.37")),
    arguments("iPad Air Case", new BigDecimal("14.99"))
    );
    }
    }

    View Slide

  32. CSV Provider
    jUnit 5 data driven
    class JUnitCSVSourceTest {
    private static final String MAXIMUM_PRICE = "30.0";
    @DisplayName("Products should not exceed the maximum price")
    @ParameterizedTest(name = "product ''{0}'' of amount ${1} does not exceeds $" + MAXIMUM_PRICE)
    @CsvFileSource(resources = "/products.csv", numLinesToSkip = 1)
    void productsLassThan(String product, BigDecimal amount) {
    assertThat(product).isNotEmpty();
    assertThat(amount).isLessThanOrEqualTo(new BigDecimal(MAXIMUM_PRICE));
    }
    }
    product,amount
    Micro SD Card 16Gb,6.09
    JBL GO 2,22.37
    iPad Air Case,14.99
    products.csv

    View Slide

  33. REFERENCES

    View Slide

  34. jUnit 5 data driven
    Main project
    https://github.com/eliasnogueira/manage-testing-data-java
    • Value Source
    • Internal Method Source
    • External Method Source
    • external method source data class
    • Argument Provider
    • argument provider data class
    • CSV Provider
    • csv file

    View Slide

  35. Main project
    https://github.com/eliasnogueira/credit-api/tree/manage-data
    • TestData Factory
    • Argument provider for edge cases
    • Tests
    test data factory

    View Slide

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

    View Slide