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

3 Design Patterns and Architecture Decisions you must use in your project

3 Design Patterns and Architecture Decisions you must use in your project

This presentation was given at QA Volume 2 Global Summit, 2021, and shows the following:

- Base Test Class: not only the basics but why you can be benefited from multiples bases tests classes

- Fluent builder: we can use it to create fluent interfaces, data objects and bring fluency in the usage of the methods

- Factory: does not matter if you are going to create a browser instance or open a new mobile device… it must be used in all targeting environments to test

- Data Generation: we can apply different strategies like the Test Data Factory, Data-Driven, or even consuming data from dynamic data sources

- Configuration management: because we don't have much time to change the code all the time we can use a proper configuration approach to avoid changes in the codebase

-Logs and reports: logs will help us to better understand what happened in that failed test, where reports will generate information that everyone can understand

Baaa72bd2671a244ba9211e015f72d28?s=128

Elias Nogueira

August 11, 2021
Tweet

Transcript

  1. 3 Design Patterns and Architecture Decisions you must use in

    your project Elias Nogueira
  2. Elias Nogueira I help professional software engineers (backend, frontend, qa)

    to develop their quality mindset and deliver bug-free software so they become top-level engineers and get hired for the best positions in the market. 🏢 Backbase 👨💻 Principal Software Engineer 📍 Utrecht, the Netherlands 🌐 eliasnogueira.com 🐦 @eliasnogueira bit.ly/eliasnogueira
  3. Base Test Class

  4. Base Test Class It is a simple approach to set

    up a shared initialization and cleanup in your tests. We commonly categorize it as a Testing pattern. The problem we are trying to solve here is the reuse of common behaviors across test classes avoiding coding duplication and centralizing these actions at one point. The application of a Base test class in OO programming languages is applied using inheritance.
  5. Base Test Class Without this approach, we have code duplicated

    related to: ◦ pre-condition ◦ post-condition ◦ any shared method TEST 1 TEST 2 TEST 3 pre-condition post-condition pre-condition post-condition pre-condition post-condition
  6. Base Test Class With this approach, we have: ◦ the

    smart use of inheritance ◦ an easy way to add more test ◦ flexible creation of test suites BASE TEST CLASS TEST 1 TEST 2 TEST 3 browser initialization/close open/close database, logs connect/disconnect servers login/logout app
  7. Examples without the usage of Base Test class ◦ FirstPageCheckTest

    ◦ TelegramCheckTest with the usage of Base Test class ◦ BaseTestWeb ◦ FristPageCheckTest ◦ TelegramCheckTest
  8. Builder and Fluent Interface

  9. Builder This pattern tries to manage the construction process of

    an object using its constructor in fluent methods. Regular class example public class Product { private final String name; private final String description; private final BigDecimal price; public Product(String name, String description, BigDecimal price) { this.name = name; this.description = description; this.price = price; } }
  10. Builder public class ProductBuilder { private String name; private String

    description; private BigDecimal price; public ProductBuilder name(String name) { this.name = name; return this; } public ProductBuilder description(String description) { this.description = description; return this; } public ProductBuilder price(BigDecimal price) { this.price = price; return this; } public Product build() { return new Product(name, description, price); } } Builder construction • All methods return the class (this) • The build() method call the constructor
  11. Fluent Interface This pattern tries to provide a readable fluent

    API over a specific domain. The domain can include more than one class. public class AmazonPage { public AmazonPage selectDepartament(Departament departament) { // some magic here return this; } public AmazonPage search(String term) { // some magic here return this; } } class FluentInterfaceTest { @Test void searchInAllDepartaments() { AmazonPage amazonPage = new AmazonPage(); amazonPage.search("Kindle Fire"); } @Test void searchOnComputers() { AmazonPage amazonPage = new AmazonPage(); amazonPage.selectDepartament(Departament.BOOKS) .search("Effective Java"); } } Fluent interface Usage
  12. Examples builder implementation ◦ Product ◦ ProductBuilder ◦ ProductBuilderTest fluent

    interface ◦ AmazonPage ◦ Department ◦ FluentInterfaceTest
  13. Factory

  14. Factory This creational pattern enables the creation of objects without

    exposing the internal logic. A good test example is the creation of different browsers instances. BROWSER FACTORY FIREFOX SAFARI EDGE CHROME
  15. Factory The basic implementation is done by having specific classes

    that will create different objects, while the main class is responsible to understand the object type to create it. More information at http://www.eliasnogueira.com/how-to-use-the-factory-design-pattern-to-create-browser-instances-the-simple-approach
  16. Examples browser factory ◦ BrowserFactory ◦ FactoryTest

  17. Data Generation

  18. Data Generation There are many ways to generate data… The

    most common are: STATIC/DYNAMIC GENERATION FAKES
  19. Fake Generation Creates an approach to generate non-sensitive data for

    your test without manually change the test data in each execution. There’re a lot of tools to create this type of data. Example with Java-Faker Faker faker = new Faker(new Locale("pt-BR")); faker.name().fullName(); faker.address().fullAddress(); faker.internet().emailAddress(); faker.business().creditCardNumber(); faker.date().birthday();
  20. Fake Generation + Data Factory We can use a Fake

    Generation in a centralized data class that can create data in any condition. public class CreditCardDataFactory { public CreditCard validCreditCard() {} public CreditCard invalidCreditCardNumber() {} public CreditCard invalidMonth() {} public CreditCard invalidYear() {} public CreditCard invalidCcv() {} }
  21. Examples data factory ◦ CreditCardDataFactory ◦ DataFactoryTest

  22. Static Generation When the data cause different behaviors in your

    application. A Static approach can be achieved by implementing any data approach like: ◦ Class ◦ CSV | JSON | TXT | YML ◦ Database ◦ Mock
  23. Data Driven It is one of the Static data generation

    types. It consists of using different data in the same test, where the data changes, not the test. Different test libraries have this approach available. Most of them consist of the data hard-coded in a test class. More info about JUnit 5 data driven methods at: https://junit.org/junit5/docs/current/user-guide/#writing-tests-parameterized-tests @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")) ); }
  24. Examples data driven ◦ CreditCardDataDriven ◦ DataDrivenTest

  25. Dynamic Generation The Dynamic approach can be implemented according to

    your context. Used to remove the maintenance of test data. Example: ◦ Queries in a database ◦ Consume data from an API
  26. Configuration Management

  27. Configuration Management It provides a way to set different values

    for a running application without the necessity of re-compile or re-run it. Normally, this approach accepts dynamic values injection through environment variables or by modifying a configuration file.
  28. Examples configuration management ◦ Configuration ◦ ConfigurationManager ◦ config.properties ◦

    WebConfigTest
  29. Logs and Reports

  30. Logs and Reports There are many ways to generate data.

    The most common are: TEST REPORT EXCEPTIONS AND BASIC LOGS
  31. Exceptions and basic logs By using any log strategy, saving

    a log file, we can understand the common errors that occurred during the test execution. These errors can be of: ◦ assertion errors ◦ timeout exceptions ◦ locator exception ◦ an exception on your architecture You can also log basic info to know some action you are doing in the code.
  32. Examples logs ◦ log4j2.properties ◦ CreditCardDataFactory ◦ 3-design-patters-arch-decision.log file generated

    in your user folder
  33. Test Report Do not matter if you will generate the

    test report in any style (below): the most important thing is to have one to satisfy your requirements. We can have it reported as: ◦ Gherkin style (readable by humans) ◦ xUnit style (readable by CI/CD tools) ◦ HTML (readable by humans) ◦ Any other style like XML, JSON, etc…
  34. Examples reports ◦ allure.properties ◦ Allure configuration placed on pom.xml

    ◦ auto-generated xUnit XML reports at target/surefire-reports
  35. Thanks! SCAN ME You can follow me on Twitter @eliasnogueira

    and find the practical examples of this presentation. Be sure to simulate the examples in the code! github.com/eliasnogueira/3-design-patters-arch-decisions