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. 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]
  2. 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]
  3. 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
  4. 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
  5. 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⃣
  6. There are some approaches to generating 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
  7. common approaches builder pattern, in the Model objects factory pattern

    to generate the data tool for dynamic data generation test data factory
  8. 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; }
  9. 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(); } }
  10. use a specialized tool Tool What it does Comment Easy

    Random Generate random data based on Java beans For a long time not updated (3 years) Uses JavaFaker 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 a more active community
  11. 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
  12. 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
  13. 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
  14. 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;
  15. 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
  16. JUnit 5 data driven Value Source Internal Method Source External

    Method Source Argument Provider CSV Provider
  17. 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); } }
  18. 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<Arguments> 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")) ); } }
  19. 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<Arguments> 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
  20. 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")) ); } }
  21. 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
  22. 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