IT eSky.pl

RSS

Izolacja testów: Dependency Injection kontra stubbing konstruktorów

By w pełni odizolować testowaną jednostkę kodu, musimy być w stanie zestubbować jej zależności. W wielu językach programowania, stubbing zakodowanych na sztywno wywołań konstruktorów jest niemożliwy. Standardowym rozwiązaniem tego problemu jest wzorzec Dependency Injection – pozwala nam on wyciągnąć zależności do parametrów, przekazywanych do testowanej jednostki kodu z zewnątrz, co z kolei umożliwia nam podmianę tych parametrów na stuby.

Jednakże, w pewnych językach, z powodu ich dynamicznej natury, stubbing zakodowanych na sztywno wywołań konstruktorów JEST możliwy (np. w językach JavaScript albo Ruby). Czy w takim razie ma sens stosowanie w tych językach Dependency Injection? Czy jest to tylko niepotrzebna komplikacja?

Przykład Dependency Injection

Przykład stubbingu konstruktorów

Zalety i wady

złożoność

Oba przykłady wyglądają niemal identycznie pod względem złożoności, jest jednak jeden element niewidoczny na powyższych przykładach: w przypadku Dependency Injection musisz w jakiś sposób wstrzyknąć zależności w Twoim produkcyjnym kodzie. Wymaga to albo użycia frameworka Dependency Injection, albo wstrzykiwania wszystkich zależności ręcznie, w jakimś wysoko-poziomowym pliku typu „bootstrap”. Tak czy inaczej, jest to bardziej skomplikowane niż w przypadku stubbowania konstruktorów – tak więc stubbowanie konstruktorów zdobywa w tej kategorii punkt.

zdolność do testowania kodu „legacy”

Kolejną przewagą stubbowania konstruktorów nad Dependency Injection jest to, że można je stosować… bez Dependency Injection. Jeśli kod legacy nie został napisany z myślą o Dependency Injection, odpowiednie dopasowanie go oznacza poważny, ryzykowny refactoring – podczas gdy stubbowanie konstruktorów jest w takim przypadku nadal możliwe, nawet dla kodu typu „big ball of mud”. Kolejny punkt dla stubbowania konstruktorów.

jawne API

Gdy przekazujesz zależność jawnie, jako parametr, staje się ona częścią publicznego API testowanej jednostki kodu. W językach dynamicznych to API nie jest tak dobrze zdefiniowane jak w językach typowanych statycznie (widzisz jedynie, że jest wymagany jakiś parametr, ale nie znasz jego typu, nie wiesz jakie powinien mieć metody itp.), jednak wciąż jest to jakaś informacja. Gdy polegasz na stubbowaniu konstruktorów, nie dostajesz żadnej informacji – nawet o tym, że w ogóle istnieje jakaś zależność. To czyni testy trudniejszymi do zrozumienia, nie jest bowiem jasne, bez wgryzania się głęboko w implementację testowanej jednostki kodu, co test stubbuje i dlaczego. Punkt za czytelność trafia więc do Dependency Injection.

kruchość

To wiąże się z jawnym API. W przypadku stubbowania konstruktorów, testy są powiązane ze szczegółami implementacji testowanej jednostki kodu. Implementacja jest znacznie bardziej podatna na zmiany niż sygnatura konstruktora. Dodatkowo, gdy modyfikujesz implementację, trudno ocenić wpływ, jaki ta zmiana będzie miała na testy – gdy zmieniasz sygnaturę konstruktora, ta relacja jest oczywista. W rezultacie, stubbowanie konstruktorów owocuje bardziej kruchymi testami – kolejny punkt trafia do Dependency Injection.

Wnioski

Wyniki mogą się na pierwszy rzut oka wydawać remisowe (2:2). Jednak korzyści obu podejść nie są tego samego „ciężaru”. Kruche i nieczytelne testy są znacznie poważniejszym problemem niż bardziej opasły bootstrap aplikacji. Dependency Injection jest wyraźnym zwycięzcą – i zapewnia wiele korzyści nawet w językach dynamicznych.

Jedyną uzasadnioną korzyścią stubbowania konstruktorów jest to, że umożliwia ono testowanie kodu „legacy”. Jednakże, powinieneś traktować takie testy jako tymczasowe testy charakteryzacyjne, które pozwolą Ci bezpiecznie zrefaktoryzować kod do czystszej, opartej na Dependency Injection struktury – pozostawienie ich zbyt długo sprawi, że Twoja suita testowa będzie trudna w utrzymaniu.

A co Ty o tym sądzisz?

Jakie są Twoje doświadczenia? Dostrzegasz jakieś inne korzyści albo wady stubbowania konstruktorów lub Dependency Injection? Którą metodę wolisz? Chętnie poznałbym Twoje zdanie!


Zobacz również