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

Devoxx 2023 - Your Tests also need some Archite...

Devoxx 2023 - Your Tests also need some Architecting

Avatar for Jonas Geiregat

Jonas Geiregat

October 02, 2023

More Decks by Jonas Geiregat

Other Decks in Technology

Transcript

  1. The reality of tests - Brittle - Complex - Hard

    to maintain - Did not guide - Cryptic - Slow - Skipped
  2. You do not only write a test in the current

    moment for the current moment
  3. - Requirements - Integrations - Security - Technology - Data

    Formats - Deployments - People - Organizations We write test to cope with change
  4. Other things are also important but if your software can

    change you can always: make it faster make it more secure make it more resilient make it more user-friendly
  5. Engineers love solving problems, that have zero business value because

    it’s considered fun. What do we write code for ?
  6. “The trigger for writing a test is not a new

    class nor a new public method” -- Ian Cooper - TDD, Where Did It All Go Wrong
  7. So you want us to only write integration tests ?

    -- Fellow developers - coffee machine
  8. unit tests end to end tests integration tests slow /

    expensive fast / cheap Does the classical testing Pyramid still stand ?
  9. The type of test: is how you are testing It

    is a technical detail, that is seldom relevant - unit - integration - acceptance - etc ..
  10. - an integration - a behavior - an implementation detail

    What are you testing Is much more relevant
  11. Requirement: An email notification should be sent to the receiver

    once an invoice has been paid Defined by the EmailService interface Implemented by the SesEmailService
  12. Test should not be viewed in the same manner one

    views production code. It’s a different context. Different rules are at play.
  13. Keep your readers in your test method Minimize setup and

    tear-down logic to only the technical
  14. Classic test naming convention class InvoiceServiceTest { class SesEmailServiceTest {

    VS Test naming convention based on requirement/feature/behavior class PayInvoiceFeatureTest { class MarkInvoiceAsProcessedFeatureTest { class CreateInvoiceTemplateFeatureTest { class ProcessInvoiceFeatureTest { class AddInvoiceLineFeatureTest {
  15. Do not communicate what you are testing @Test void testExpireInvoice()

    { @Test void addPineappleToppingToPizzaTest() {
  16. Don’t communicate what you are testing but why you need

    a test The why captures the behaviour
  17. The why @Test void testExpireInvoice() { @Test void unExpiredInvoice_shouldExpireAutomatically_after30DaysAfterPublicationDate() {

    @Test void addPineappleToppingToPizzaTest() { @Test void pineappleIsAnIllegalTopping_andShouldPreventTheCookFromAddingIt() {
  18. Address billingAddress = new Address.Builder() .streetAddress("123 Main St") .city("Anytown") .state("CA")

    .zipCode("12345") .country(Country.US) .build(); Address shippingAddress = new Address.Builder() .streetAddress("456 Oak Ave") .city("Othertown") .state("CA") .zipCode("67890") .country(Country.US) .build(); List<InvoiceItem> items = new ArrayList<>(); items.add(new InvoiceItem("Product A", 100.00, Tax.vatPercentage(21))); items.add(new InvoiceItem("Product B", 100.00, Tax.vatPercentage(21))); Customer customer = new Customer.Builder() .name("John Doe") .email("[email protected]") .phoneNumber("555-123-4567") .build(); Invoice invoice = new Invoice.Builder() .invoiceNumber("001") .customer(customer) .billingAddress(billingAddress) .shippingAddress(shippingAddress) .invoiceDate(LocalDate.now()) .items(items) .build(); var amount = invoice.getVatAmount(); assertThat(amount).isEqualTo(Amount.EUR(42))
  19. // given Address billingAddress = new Address.Builder() .streetAddress("123 Main St")

    .city("Anytown") .state("CA") .zipCode("12345") .country(Country.USA) .build(); Address shippingAddress = new Address.Builder() .streetAddress("456 Oak Ave") .city("Othertown") .state("CA") .zipCode("67890") .country(Country.USA) .build(); List<InvoiceItem> items = new ArrayList<>(); items.add(new InvoiceItem("Product A", 100.00, Tax.vatPercentage(21))); items.add(new InvoiceItem("Product B", 100.00, Tax.vatPercentage(21))); Customer customer = new Customer.Builder() .name("John Doe") .email("[email protected]") .phoneNumber("555-123-4567") .build(); Invoice invoice = new Invoice.Builder() .invoiceNumber("001") .customer(customer) .billingAddress(billingAddress) .shippingAddress(shippingAddress) .invoiceDate(LocalDate.now()) .items(items) .build(); // when var amount = invoice.getVatAmount(); // then assertThat(amount).isEqualTo(Amount.EUR(42))
  20. InvoiceMother.validInvoice() <InvoiceMother.Builder> .withInvoiceItems( new InvoiceItem("Product A", Amount.EUR(100), Tax.vatPercentage(21)), new InvoiceItem("Product

    B", Amount.EUR(100), Tax.vatPercentage(21))) .build(); <Invoice> Emphasize what matters and hide the irrelevant parts returns a prefilled builder that allows you to override what is relevant
  21. InvoiceMother.validInvoice(); InvoiceMother.validInvoiceWithUSShippingAddress(); InvoiceMother.validInvoiceFromYesterday(); InvoiceMother.validInvoiceWithInvoiceLines(10); InvoiceMother.validInvoice() <InvoiceMother.Builder> .withInvoiceItems( new InvoiceItem("Product A",

    Amount.EUR(100), Tax.vatPercentage(21)), new InvoiceItem("Product B", Amount.EUR(100), Tax.vatPercentage(21))) .build(); <Invoice> Limit your factory methods, override what matters with setters InvoiceMother.validInvoice() .withShippingAddress(address -> address.country(Country.US)) .build() Emphasize what matters and hide the irrelevant parts
  22. //given var invoice = InvoiceMother.validInvoice() .withInvoiceItems( new InvoiceItem("Product A", Amount.EUR(100),

    Tax.vatPercentage(21)), new InvoiceItem("Product B", Amount.EUR(100), Tax.vatPercentage(21))) .build(); // when var amount = invoice.getVatAmount(); // then assertThat(amount).isEqualTo(Amount.EUR(42)) Emphasize what matters and hide the irrelevant details
  23. Change is inevitable that is why we test Behavior is

    what we test Accepting that tests live in a different context is how we test
  24. Test and production code should be equal citizens looked at

    from different context with equal architectural rights cared for equally equally important