Czy TDD chroni przed głupotą?

9

 

Często słyszę: “TDD powinno sprawiać, że oprogramowanie nie ma bugów”. To bardzo mylne pojmowanie wszystkiego, co się za TDD kryje. I dla tej praktyki mocno krzywdzące, bo gdy się okazuje, że tak nie jest, to ludzie się zniechęcają. Starałem się zdecydowanie podkreślać ten fakt podczas moich wystąpień na temat unit testów w zeszłym i za-przeszłym roku. Może w kontekście ostatnich wydarzeń w świecie unit-testów warto by zorganizować kolejny touree-de-pologne z tym tematem? 😉

Ale do rzeczy: czy TDD zwalnia z myślenia? Albo inaczej: czy z TDD nie można popełnić błędów? Czy przetestowany kod jest poprawny? To również było jednym z moich głównych punktów podczas prezentacji: testy weryfikują, że kod działa tak jak chce tego programista, a nie tak jak powinien!

Zamiast się nad tym dalej rozwodzić, przytoczę przykład sprzed kilku dni. Ujawnię swój własny wstydliwy zakamar. Miałem taki interfejs:

public interface IDetectInvalidOrders
{
    bool IsValid(Order order);
}

A zaimplementowałem to tak:

public class InvalidOrdersDetector : IDetectInvalidOrders
{
    public bool IsValid(Order order)
    {
        return order.Description == "invalid-order";
    }
}

Logika sama w sobie jest prawidłowa: “biznesową niepoprawność” zamówienia w tym systemie oznacza się poprzez wpisanie w nim jakiegoś zdefiniowanego opisu, który w prawdziwym systemie pobieram z konfiguracji ale tutaj to uprościłem.

Testy? Owszem, są! Ba, były nawet napisane najpierw:

public class InvalidOrdersDetectorTests
{
    readonly InvalidOrdersDetector _detector;
    readonly Order _order;

    public InvalidOrdersDetectorTests()
    {
        _order = new Order();
        _detector = new InvalidOrdersDetector();
    }

    bool execute()
    {
        return _detector.IsValid(_order);
    }

    [Fact]
    public void detects_invalid_order_based_on_description()
    {
        _order.Description = "invalid-order";

        var result = execute();

        result.ShouldBe(true);
    }

    [Fact]
    public void does_not_detect_order_with_no_description_as_invalid()
    {
        var result = execute();

        result.ShouldBe(false);
    }

    [Fact]
    public void does_not_detect_order_with_other_description_as_invalid()
    {
        _order.Description = "other description";

        var result = execute();

        result.ShouldBe(false);
    }

}

Super. Kilkanaście commitów później sklejam wszystko w całość i uruchamiam. Damn, nie działa! Jak to możliwe?

Z kwadrans zajęło mi dojście do źródła problemu. Co z tego że mam testy, skoro one testują ODWROTNY sposób działania tego “detectora”? Widzicie? Metoda o nazwie “IsValid” sprawdza, czy zamówienie jest “invalid”. A reszta kodu korzysta z niej zgodnie z nazwą…

W napisanym, przetestowanym, zacommitowanym kodzie mam taką głupotę, że aż strach. Moja głowa musiała gdzieś odfrunąć. Mózg się wyłączył, a rency sami klepali testy a potem implementację.

Na szczęście wykryłem ten fakt przed “git push”, więc mogłem poprawić commit żeby nie było wstydu. Co prawda wstyd i tak będzie, bo w końcu jest ten post, ale… chyba warto. Przykład z życia, że jak ktoś się nawet na chwilę zmienia przed komputerem w debiloidiotę, to i TDD nie pomoże.

Odpowiedź zatem jest prosta: nie, TDD nie chroni przed głupotą. TDD nie zwalnia z myślenia. TDD nie zrobi z nas geniuszy.

Share.

About Author

Programista, trener, prelegent, pasjonat, blogger. Autor jedynego polskiego podcasta programistycznego: DevTalk.pl. Jeden z liderów Białostockiej Grupy .NET. Od 2008 Microsoft MVP w kategorii .NET. Więcej informacji znajdziesz na stronie O autorze. Napisz do mnie ze strony Kontakt. Dodatkowo: Twitter, Facebook.

9 Comments

  1. Pingback: dotnetomaniak.pl

  2. Trochę mi ulżyło, że nie tylko ja takie ciekawostki czasem popełniam 😉

    Całość postu uogólnił bym nawet bardziej, że nie tyle TDD nie ratuje, żadne unit testy (nie ważne jak duże jest pokrycie) nie zwalniają z myślenia.

  3. Po pierwsze duże pokrycie nie oznacza brak bugów. Nie jeden raz miałem tak, że piszę mnóstwo unit testów na niewielką metodę (~50ln) i cieszę się jaki jaki mój kod jest pokryty. A potem na testach i tak wychodzą bugi.

    Po drugie, unit testy to też kod i pytanie jakie testy będą testować unit testy? :) Tego już chyba nie ominiemy.

    Maćku, myślę, że w twoim wypadku odpowiednie nazwanie testu mogłoby pomóc wykryć błąd np. order_with_desc_invalid_should_be_valid. Po przeczytaniu czegoś takiego widać, że coś jest nie halo :)

  4. Poprawiłeś testowaną metodę i testy i zweryfikowałeś poprawkę. A więc można powiedzieć, że dzięki testom możesz łatwiej zweryfikować poprawki blędów, które wprowadziłeś podczas rozwijania kodu z użyciem TDD :)

  5. Marcin,
    Popełnia każdy :) A co do zwolnienia z myślenia to… nic przed tym nie zwalnia, nie tylko testy.

  6. ekdemeo,
    Masz rację, ale jak się mózg wyłącza to nic nie poradzisz :). A co do testowania testów to, szczerze mówiąc, czasami zdarza mi się coś takiego popełnić. Bardzo rzadko co prawda, ale jednak jakiś testowy “helper” również otestowuję.

  7. Piotr Perak,
    TAK! I to jest największa korzyść płynąca z TDD moim zdaniem: ułatwione wprowadzanie poprawek do systemu, utrzymywanie, ogólnie: zmiany.

  8. PaSkol,
    Jak najbardziej, w ogóle głupota może wkraść się wszędzie, niezależnie od zastosowanej metody kodowania.