Często słyszymy: “TDD powinno sprawiać, że oprogramowanie nie ma bugów“. A 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ą.
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? Uwaga, rocket science approaching:
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. 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ą…
Uwaga! Z wielką przyjemnością ogłaszam Inicjatywę SmartTesting.pl i serdecznie Cię na nią zapraszam! Tam wraz z TOP Expertami będziemy dostarczać masę rewelacyjnych materiałów o testach!
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.
Happy testing!
Czy TDD chroni przed głupotą? | Maciej Aniserowicz o programowaniu…
Dziękujemy za dodanie artykułu – Trackback z dotnetomaniak.pl…
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.
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 :)
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 :)
To co opisujesz – rozbieżność pomiędzy nazwą metody, a jej działaniem może wystąpić w dowolnym kodzie, nie tylko w tym zawierajacym testy jednostkowe (pisałem o tym kiedyś: http://paskol.robi.to/?p=1382).
Marcin,
Popełnia każdy :) A co do zwolnienia z myślenia to… nic przed tym nie zwalnia, nie tylko testy.
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ę.
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.
PaSkol,
Jak najbardziej, w ogóle głupota może wkraść się wszędzie, niezależnie od zastosowanej metody kodowania.
Ja bym jeszcze dodał że poprawne testy nie gwarantują poprawnego działania kodu, tylko wykazują błędy w testowanym scenariuszu.