Posts Tagged ‘tdd’

Learn how to visualize complex input parameters in parameterized tests in the way improving the readability of the test report.

Trimmed Hedge

By Tomwsulcer – CC0, Wikipedia

Introduction

Parameterized tests can simplify the way how the same functionality can be verified with different input parameters. Spock with its where block, data tables and data pipes makes it very easy to use in a very readable way. The input parameters are nicely presented in a test execution report (in IDE, Jenkins or generated HTML). They can be formatted in a desired order and completed with a custom constant message. It usually works flawlessly for simple objects (such as numbers, booleans, enums and strings). However, in the situation where complex objects are used (e.g. bigger value objects or custom classes) the whole output can be hijacked by just one very verbose parameter:

Very long full toString

or meaningless default toString() implementation: foo.bar.Unknown@78ac1102:

Meaningless Default toString in tests

Our sample code base

To present different available approaches I will use a very simplified version of an account & invoice related domain implemented with DDD in one of the projects I worked in.

The main class here is Invoice which represents an invoice :). The object is immutable (here with Groovy AST transformation, but it could be also achieved with Project Lombok or manually) which means that methods modifying a state return a new version of this class.

@Immutable
class Invoice {

    enum Status {
        ISSUED, PAID, OVERDUE, CANCELLED
    }

    Status status
    BigDecimal issuedAmount
    BigDecimal remainingAmount
    LocalDate issueDate
    //some other fields

    //different production methods

    //production toString() with all useful business fields
}

There is also Account class with an amountToPay() method returning amount to pay based in open invoices.

Naive approach (strongly not recommended)

As the first idea one could be tempted to modify toString() method implementation to, for example, display only 2 of the 10 fields in a class. However, it is a bad idea to change production toString() just for the better output in tests. What is more, other tests or error reporting in a production system can prefer to display more information. Luckily in Spock we have two nice techniques to cope with it.

Technique 1 – an extra formatting method

Test/specification names in Spock can be enhanced with #parameterName (not with $ character used internally by Groovy which is not allowed in a method name) placed in a test name or in an @Unroll annotation. In addition there is an ability to use object property value or call a parameterless method.

class AmountToPayInvoiceAccountSpec extends Specification {

    def "paid and cancelled invoices (#invoice.formatForTest()) should be ignored in current amount to pay"() {
        given:
            Account account = AccountTestFixture.createForInvoice(invoice)
        expect:
            account.amountToPay() == 0.0
        where:
            invoice << [paidInvoice, cancelledInvoice]
    }

    (...)
}

//required modifications in production code
class Invoice {

   (...)

   String formatForTest() {
       return "$issuedAmount: $status"
   }

Test specific production method - result

It’s nice to get just two fields from an object, however, in many cases we don’t want to add an artificial formatting method to production code just to be used in tests.

A tip. Don’t forget to enable unrolling of paremterized tests to instruct Spock to create a separate (sub)test for every input parameters set. It can be done manually by placing @Unroll annotation on an every parameterized method or at the class level. Alternatively the spock-global-unroll extension can be used to turn it on automatically in the whole project.

Technique 2 – an extra input parameter

Luckily, as an alternative it is possible to define an another artificial input parameter directly in a test. It looks like a ordinarily variable, but has access to a set of input parameters (for a given iteration) and can operate on they. That extra parameter is treated by Spock equally to others (however usually there is no need to reference to it in a test code – beside a test name).

class AmountToPayInvoiceAccountSpec extends Specification {

    @Shared
    private Invoice first = createOpenForAmount(200)
    @Shared
    private Invoice second = createOpenForAmount(300)

    def "current amount to pay (#expectedToPayAmount) should ignore paid and cancelled invoices (#invoicesDesc)"() {
        given:
            Account account = AccountTestFixture.createForInvoices(invoices)
        expect:
            account.amountToPay() == expectedToPayAmount
        where:
            invoices                   || expectedToPayAmount
            [paid(first), second]      || 300.0
            [first, cancelled(second)] || 200.0
            [first, second]            || 500.0

            invoicesDesc = createInvoicesDesc(invoices)
    }

    (...)
}

Implementation note. Methods createOpenForAmount() as well as paid() and cancelled() are implemented in a test specific InvoiceTestFixture class.

The result looks very nicely:
spock-formatting-input-parameters-test-specified-result

Just from the report it is pretty visible that there is a (regression) issue with handling CANELLED invoices. The assertion error is also helpful:

spock-formatting-input-parameters-test-specified-error-message

It’s worth to notice in this place that this technique can be also mixed with data pipes (in addition to data tables):

    where:
        invoice << [paidInvoice, cancelledInvoice]
        invoiceDesc = createInvoiceDesc(invoice)

A tip. Pay attention that in opposite to regular parameters in Spock the artificial one is created with an = operator not with <<.

Summary

The aforementioned techniques can be used to improve the readability of your test execution report. It’s useful during the development, but what is even more important it becomes indispensable if Spock is used for Behavior Driven Development and reports are read by, so called, Business People (i.e. need to be worded in the specific way).

[OT] The reason to bring up this topic is a fact that recently two colleagues of mine were struggling with that issue in their tests. Unfortunately they overlooked that slide in my advanced Spock presentation at Gr8Conf EU ;). Blessing in disguise, I was in the office to support them immediately. Nevertheless, not so long ago I’ve seen a presentation by Scott Hanselman about productivity. I liked the idea that every good question is worth to be answered on a blog. Replying privately (especially via email) usually can help only just one person. Writing a blog post and sending that person a link in addition can help other people struggling with the same issue.

Self promotion. Would you like to improve your and your team testing skills and knowledge of Spock quickly and efficiently? I conduct a condensed (unit) testing training which you may find useful.

Uwaga. Wymienione czynności mogą mieć wiele określeń. Trzeba brać poprawkę, iż podane słownictwo nie jest jedynym słusznym.

Coding Dojo (a.k.a. Code Dojo) jest spotkaniem, na którym grupa ludzi rozwiązuje zadania programistyczne najczęściej w formie Code Kata. Jego celem – oprócz dobrej zabawy – jest wymienianie się praktykami i szlifowanie przydatnych na co dzień umiejętności. Do przeprowadzenia spotkania potrzebne są:
  – sala z miejscami siedzącymi
  – komputer do rozwiązywania zadania (laptop)
  – rzutnik/projektor

Popularnymi rodzajami spotkań są Prepared Kata i Randori Kata (nazwa wywodzi się techniki stosowanej przy ćwiczeniach sztuk walk). Obydwa polegają na implementacji rozwiązania dla postawionego (zazwyczaj prostego) zadania z wykorzystaniem TDD i jednorazowym robieniem małych kroków. Istotne jest, aby każdy na sali orientował się, co w danej chwili jest robione i z czego to wynika. W przypadków pogubienia wskazane jest zadawanie pytań. Wtedy prace implementacyjne powinny być wstrzymane, aż do wyjaśnienia wszelkich wątpliwości.

W przypadku Prepared Kata prowadzący siedzi przed komputerem i wykonuje kolejne kroki: pisze test, implementuje go, dokonuje refaktoringu (w myśl mantry TDD: red, green, refactor). Osoby na sali biorą udział w proponowaniu następnych posunięć. Spotkanie takie jest bardzo przydatne przy wprowadzaniu zasad wśród ludzi, którzy nie mieli wcześniej do czynienia z TDD.

Randori Kata jest bardziej zaawansowaną wersją Prepared Kata. Przy komputerze siedzą jednocześnie dwie osoby, które wymieniają się zadaniami (test – implementacja – refaktoring). Co określony czas (na przykład 5-7 minut) osoba dłużej przebywająca przy komputerze wraca na salę, a w zamian przychodzi ktoś nowy. Jako wariant może istnieć bardziej sztywny podział na role w danej parze (pilot i nawigator). Na zakończenie iteracji pilot wraca na salę, a nawigator przejmuje jego rolę. Osoby nie biorące w danej chwili bezpośredniego udziału w implementacji mogą sugerować rozwiązania i zgłaszać uwagi. Ten typ spotkań jest przeznaczone dla bardziej zaawansowanych grup, aby zapewnić akceptowalny dla wszystkich na sali postęp prac.

Opracowanie zostało przygotowane na podstawie opisu ze strony Coding Dojo, gdzie można znaleźć również szereg zagadnień nadających się do rozwiązania na spotkaniach Coding Dojo.

Tego typu działania są praktykowane w ramach spotkań grupy Warszawa-DP zrzeszającej osoby z Warszawy i okolic zainteresowane wymianą doświadczeń i podnoszeniem swoich umiejętności w zakresie tworzenia dobrego kodu. Na spotkania może przyjść każdy, udział w nich jest bezpłatny. Więcej informacji można uzyskać na liście mailingowej grupy.