Kocham UnitTesty*


Dziś stanąłem przed zadaniem poprawienia komponentu, który automatyzuje nam kwestię wyszukiwania elementów w listingach.

Jego działanie można opisać z grubsza tak:
Na przykład jeśli chcę, żeby moja lista kosztów (Cost) mogła być wyszukiwana po nazwach – dodaję formularz z Cost.name i w akcji kontrolera wywołuję jedynie

$conditions = $this->Search->getConditions($this->params);
$this->paginate("conditions"=>$conditions);
$this->set('costs', $this->paginate('Cost'));

Największy bajer polegał na tym, że jeżeli moje koszty były powiązane z samochodami (Car) to chcą wyszukiwać po nazwie samochodu wystarczyło, że dodałem do formularza pole Car.name. Mój komponent potrafił zorientować się, że to jest powiązany model, wyszukać elementy spełniające warunek, a następnie zwrócić w conditions coś takiego:

Array
(
    [and] => Array
        (
            [Cost.id] => Array
                (
                    [0] => 1
                    [1] => 2
                    [2] => 5
                    [3] => 9
                    [4] => 10
                    [5] => 13
                    [6] => 17
                    [7] => 18
                )

        )

)

Gdzie cake ładnie sobie to przerabiał na zapytanie (w przybliżeniu)

Select * from costs as Cost where Cost.id IN (1, 2, 5, 9, 10, 13, 17, 18)

Byłem bardzo zadowolony z komponentu. Służył nam dzielnie przez przynajmniej dwie ostatnie iteracje. Każdą wyszukiwarkę/filtr dodawaliśmy w 5 minut na żądanie klienta (wewnętrznego ;)).

Mało tego, dość dużym wyzwaniem było sporządzenie testów dla tego komponentu (kilka MockObjects nawet się tam pojawiło) – pewnie dlatego tak sumiennie je wykonałem – 91% (z hakiem!) pokrycia kodu przez testy.

Przyszedł jednak dzień, gdy podczas prezentacji iteracji szef (klient wewnętrzny) powiedział (cytat nie dosłowny):
“Chcę jeszcze filtrować koszty wg numerów faktur (Invoice.number) z którymi te koszty są powiązane”

Problem w tym, że koszty są powiązane z fakturami poprzez elementy faktur (InvoiceElement), a konkretniej

Cost habtm InvoiceElement belongsTo Invoice

Zatem przyszło mi poprawić komponent, przy czym szczęśliwie zdążyłem zapomnieć wszystkich niuansów, foreachów i innych takich, które harcowały wewnątrz search_component.php. Myślę sobie – ok, tak jak poprzednio – dużo testów. Ale nie wiedziałem jak się do nich zabrać. Wiedziałem jedynie, że nazwa pola dla nowego przypadku powinna zawierać ścieżkę powiązań (np. InvoiceElement.Invoice.number).

Mając jednak w głowie dość przydatną radę na temat unit testów, która brzmi:

Najlepsze są testy napisane przed kodem. Jednocześnie napisanie najpierw kodu, a potem testów jest o niebo lepsze od braku jakichkolwiek testów.

Dlatego postanowiłem od razu przejść do próby implementacji nowych elementów dbając jedynie o kondycję dotychczasowych testów.

To co się wydarzyło można nazwać chyba “uprzężą testową”. Mogłem odważnie śmigać po dotychczasowo napisanym kodzie, bo w tym samym czasie zielone pole w rezultatach testów mówiło mi “śmiało dalej! niczego do tej pory nie popsułeś!”. Wspaniałe uczucie.

Wprawdzie po skończeniu pokrycie kodu spadło do 80% (nota bene i tak świetny wynik, 70% uznaje się za przyzwoite pokrycie), ale zaraz po napisaniu tego postu zabieram się za sporządzenie przynajmniej kilku testów. Trzeba zdążyć przed urlopem ;)

(*) Ta miłość jest nieodwzajemniona, gdy temat rozmowy schodzi na Fixture’y. Pewnie dlatego ten komponent testuje się tak ładnie i szybko – nie korzysta z bazy, tylko z Mock’owanych obiektów.

Share Button

Leave a Reply

Your email address will not be published. Required fields are marked *