16

Po tym, co przeczytałem na temat Dependency Injection i IoC, zdecydowałem się spróbować użyć Windsor Container w naszej aplikacji (jest to wielowarstwowa aplikacja sieciowa 50K LOC, więc mam nadzieję, że nie jest to przesada). Użyłem prostej klasy statycznej do zawijania kontenera i zainicjowałem ją podczas uruchamiania aplikacji, co na razie działa całkiem dobrze.Jaka powinna być strategia testowania jednostek podczas korzystania z IoC?

Moje pytanie dotyczy testów jednostkowych. Wiem, że DI sprawi, że moje życie będzie o wiele łatwiejsze, dając mi możliwość wstrzyknięcia niedokładnych/mockowych implementacji współpracowników klasy do testowanej klasy. Napisałem już kilka testów przy użyciu tej techniki i wydaje mi się, że to ma sens. Nie jestem pewien, czy powinienem używać IoC (w tym przypadku Windsor Castle) również w testach jednostkowych (prawdopodobnie jakoś skonfigurować go tak, aby zwracał kody/makiety dla moich specjalnych przypadków), czy lepiej połączyć wszystkie zależności ręcznie w testach. Co sądzisz i jaka praktyka dla ciebie działa?

+2

Duplikat: http://stackoverflow.com/questions/1465849/using-ioc- for-unittesting –

+0

Dzięki, nie mogłem znajdź go gdziekolwiek;) –

Odpowiedz

20

Nie potrzebujesz pojemnika DI w testach jednostkowych, ponieważ zależności są zapewniane przez fałszywe obiekty wygenerowane za pomocą struktur, takich jak Rhino Mocks lub Moq. Tak więc, na przykład, gdy testujesz klasę, która ma zależność od jakiegoś interfejsu, ta zależność jest zwykle dostarczana przez wtrysk konstruktora.

public class SomeClassToTest 
{ 
    private readonly ISomeDependentObject _dep; 
    public SomeClassToTest(ISomeDependentObject dep) 
    { 
     _dep = dep; 
    } 

    public int SomeMethodToTest() 
    { 
     return _dep.Method1() + _dep.Method2(); 
    } 
} 

W aplikacji można użyć ramy DI przekazać jakąś rzeczywistą realizację ISomeDependentObject w konstruktorze, który mógłby sam mieć współzależności z innymi obiektami, podczas gdy w badanej jednostki tworzenia makiety obiektu, ponieważ chcesz tylko przetestować ta klasa w izolacji. Przykład z Rhino Mocks:

[TestMethod] 
public void SomeClassToTest_SomeMethodToTest() 
{ 
    // arrange 
    var depStub = MockRepository.CreateStub<ISomeDependentObject>(); 
    var sut = new SomeClassToTest(depStub); 
    depStub.Stub(x => x.Method1()).Return(1); 
    depStub.Stub(x => x.Method2()).Return(2); 

    // act 
    var actual = sut.SomeMethodToTest(); 

    // assert 
    Assert.AreEqual(3, actual); 
} 
+0

Brzmi rozsądnie. W końcu to właśnie robię. Po prostu nie byłem pewien, czy to dobra praktyka, zwłaszcza gdy istnieje wiele zależności i muszę ręcznie kpić z nich wszystkich. –

+0

Spójrz na automocking, o którym mówiłem w mojej odpowiedzi Thomas, to naprawdę upraszcza testowanie klas z wieloma zależnościami. :-) –

2

Właśnie napisałem bardzo podobny styl i wielkość aplikacji. W testach jednostkowych nie wprowadziłbym żadnego zastrzyku zależności, ponieważ nie jest on wystarczająco skomplikowany, aby być koniecznym. Powinieneś używać szyderczego frameworka do tworzenia swoich mocks (RhinoMocks/Moq).

Również Automocking w Moq lub Auto Mock Container w Rhinomock uprości budowę twoich makiet dalej.

Auto szyderczy pozwala uzyskać obiekt typu chcesz przetestować bez konfigurowania mocks ręcznie. Wszystkie zależności są automatycznie wyśmiewane (zakładając, że są to interfejsy) i wstrzyknięte do konstruktora typów. Jeśli chcesz, możesz ustawić oczekiwane zachowanie , ale nie musisz tego robić.

+0

Tak, ale czy nie jest to również pojemnik IoC? ;) Po prostu jest bardziej swobodny w rozwiązywaniu zależności: generuje domyślną próbną implementację usług bez wyrzucania wyjątków dla nierozwiązanych rzeczy, takich jak "właściwy" kontener Windsor. –

+1

... a co najważniejsze tworzy się tylko dwie linie! Fakt, że nie trzeba rejestrować żadnych zależności, oznacza, że ​​jest on łatwiejszy niż zwykły IoC. Innym powodem używania automockingu jest jednak wyodrębnienie wywołania do obiektu w ramach konstruktora testowego. Dlatego też, jeśli zmiany w podpisie nie muszą zmieniać wielu testów, dzięki czemu twoje klasy testowe będą mniej kruche. :-) –

3

Pracuję nad projektem ASP.NET MVC z około 400 testów jednostkowych. Używam programu Ninject do wstrzykiwania zależności i MBUnit do testowania.

Ninject nie jest konieczny do testowania jednostkowego, ale zmniejsza ilość kodu, który muszę wpisać. Muszę tylko raz określić (dla każdego projektu), jak należy tworzyć instancje moich interfejsów, a nie robić to za każdym razem, gdy inicjuję testowaną klasę.

Aby zaoszczędzić czas na pisanie nowych testów, stworzyłem klasy bazowe urządzeń testowych z możliwie dużym ogólnym kodem konfiguracji. Procedury konfiguracyjne w tych klasach inicjują fałszywe repozytoria, tworzą pewne dane testowe i fałszywą tożsamość dla testowego użytkownika. Testy jednostkowe inicjują tylko dane, które są zbyt szczegółowe, aby przejść do ogólnych procedur konfiguracji.

Ja także kpię z przedmiotów (w przeciwieństwie do fałszowania) w niektórych testach, ale okazało się, że fałszowanie repozytoriów danych powoduje mniej pracy i dokładniejsze testy.

Na przykład trudniej byłoby sprawdzić, czy testowana metoda właściwie zatwierdza wszystkie aktualizacje repozytorium po ich wykonaniu podczas korzystania z repozytorium szyderczego, niż gdy używam fałszywego repozytorium.

Przygotowanie na początek było dość pracochłonne, ale naprawdę pomogło mi zaoszczędzić sporo czasu na dłuższą metę.

0

Jak już zauważył Darin, nie musisz używać DI, jeśli masz drwiny. (Jednak DI ma także kilka innych zalet, w tym przede wszystkim zmniejszenie zależności w kodzie, co znacznie ułatwia utrzymanie kodu i jego przedłużenie na dłuższą metę).

Ja osobiście wolę okablowanie wszystkiego w moich testach jednostkowych, polegając w jak najmniejszym stopniu na zewnętrznych frameworkach, plikach konfiguracyjnych itp.

Powiązane problemy