7

Używam Microsoft testowanej jednostki i są następujące:Unit Testing obiekt, którego żywotność zakres jest obsługiwane przez IoC kontenera

public class AccountCommandHandlers : 
    Handler<CreateAccountCommand>, 
    Handler<CloseAccountCommand> 
{ 
    public bool CreateAccountCommandWasCalled = false; 
    public bool CloseAccountCommandWasCalled = false; 

    public void Handle(CreateAccountCommand command) 
    { 
     CreateAccountCommandWasCalled = true; 
    } 

    public void Handle(CloseAccountCommand command) 
    { 
     CloseAccountCommandWasCalled = true; 
    } 
} 

[TestMethod] 
public void CanRaiseInternalHandlers() 
{ 
    var iocContainer = SimpleInjectorWiringForMembus.Instance; 
    iocContainer.Bootstrap(
     AppDomain.CurrentDomain.GetAssemblies()); 

    var membus = MembusWiring.Instance; 
    membus.Bootstrap(); 

    membus.Bus.Publish(new CreateAccountCommand() { Id = 100 }); 
    membus.Bus.Publish(new CloseAccountCommand() { Id = 100 }); 
} 

Używam kontenera IoC (Simple wtryskiwacza), która obsługuje zakres dożywotnią obiektów. Membus przekazuje polecenia do programów obsługi komend i rozwiązuje je za pośrednictwem kontenera IoC.

Powyższy kod działa i działa, a procedury obsługi komend ustawiają zmienne lokalne na true.

Ponieważ jednak Simple Injector obsługuje zakres życia, nie mogę prosić o Simple Injector dla obiektu AccountCommandHandler, ponieważ zwróciłby nowy obiekt z CreateAccountCommandWasCalled ustawionym na false.

Będąc nowicjuszem w jednostce Testowanie, co byłoby bardziej niezawodnym sposobem testowania niż ustawienie CreateAccountCommandWasCalled jako zmiennej statycznej?

Odpowiedz

5

Tak jak wspomnieli już inni, faktycznie przeprowadzacie testy integracji. Nie stanowi to jednak problemu. Testy integracyjne są odpowiednie do testowania ustawień IoC i do upewnienia się, że różne części aplikacji współpracują ze sobą.

Jednak przy testach integracyjnych nie należy używać próbnych lub pośrednich obiektów. Mocks i stub mają swoje zastosowania w testowaniu jednostkowym. Test jednostkowy polega na przetestowaniu najmniejszej możliwej części kodu. W ramach testu jednostkowego używasz mocks do kontrolowania zachowania wszystkich zależności, które posiada twoja klasa. Napisałem blog rok temu, co stanowi wprowadzenie do różnic między testami integracyjnymi i jednostkowymi oraz jak używać szyderstwa w testach.

W twojej sytuacji nie używałbym kontenera IoC z konfiguracją produkcyjną do konfigurowania testów jednostkowych. Zamiast tego przełączyłem się na ręczne tworzenie obiektów w testach i używanie narzędzia szyderczego, takiego jak Moq do kontrolowania zależności.

Ale to także coś, co można zautomatyzować. Świetnym narzędziem jest AutoFixture. "Fixture" odnosi się do linii bazowej, której potrzebujesz do przeprowadzenia testów. Mogą to być niektóre przykładowe dane, mocks i kody pośredniczące, których potrzebujesz i inny kod instalacyjny.

Mark Seemann (twórca AutoFixture) napisał miły blog kilka tygodni temu o użyciu AutoFixture wraz z IoC jako Auto-mocking Container. Radziłbym użyć czegoś takiego, żeby zorganizować testy jednostek.

5

Oto bardziej „filozoficzna” odpowiedź na pytanie :-)

Moja rekomendacja byłoby nie skorzystać z kontenera IOC w testach w ogóle, jeśli to możliwe!

Moją przesłanką jest to, że potrzebujesz testu, aby mieć pełną kontrolę nad kontekstem testu, a IOC może odebrać część tej kontroli. IMO, testy jednostkowe powinny być tak skoncentrowane, małe i przewidywalne, jak to możliwe!

Rozważ wysłanie próbnych obiektów do testowanej klasy zamiast do rzeczywistych zajęć.

Jeśli twoja klasa musi mieć wewnętrzną instancję kontenera IOC, pomóż ją klasie w "kontrolerze" jakiegoś rodzaju.

Możesz to zrobić na kilka sposobów, moim ulubionym jest używanie architektury podobnej do Rhino Mocks.

W ten sposób faktycznie odcinałbyś "cykl życia" dostarczony przez IOC w czasie wykonywania, w twoim teście "setup".

Test powinien zatem mieć pełną kontrolę (poprzez kpiny i stubbing) podczas tworzenia lub niszczenia obiektów przy użyciu szkieletu, takiego jak Rhino.

Można wyśmiewać MKOl, jeśli jest to potrzebne.

Na marginesie, jedną z korzyści dobrze zaprojektowanego kontenera IOC jest to, że powinno ono ułatwić testowanie jednostkowe - ponieważ powinno zniechęcać klasy do polegania na faktycznych "konkretnych przypadkach" klas i zachęca do stosowania wymiennych zamiast tego interfejsy.

Powinieneś spróbować polegać w czasie pracy na kontenerze IOC, zapewniając konkretne implementacje interfejsów, które zaprojektowałeś.

Należy pamiętać, że zwykle ważne jest, aby uzyskać jasność co do tego, co faktycznie testujesz. Testy jednostkowe powinny zazwyczaj skupiać się na testowaniu zachowania pojedynczej metody na jednej klasie.

Jeśli faktycznie testujesz "więcej" niż tylko jedną metodę na jednej klasie, na przykład: jak klasa współdziała z innymi klasami, oznacza to, że najprawdopodobniej piszesz test "integracyjny", a nie prawdziwy test "jednostki".

Kolejna uwaga: nie twierdzę, że jestem ekspertem od testowania jednostkowego!Są niesamowicie użyteczne, ale wciąż mam problemy z testowaniem więcej niż jakikolwiek inny aspekt kodowania.

Do dalszego czytania gorąco polecam "The Art of Unit Testing" Roya Osherove'a. Są też inni.

The Art of Unit Testing

+0

OK, ta odpowiedź jest bardziej podobna do wielkiej krytyki dotyczącej testów jednostkowych niż odpowiedzi na bardzo konkretne pytanie, przepraszam! Mam nadzieję, że ktoś kiedyś okaże się przydatny :) – GrahamMc

+0

Zgadzam się z tym, co tu mówisz, ale mam wrażenie, że OP faktycznie pisze test integracji, aby sprawdzić, czy system będzie działał poprawnie. W tych niewielu przypadkach warto przetestować infrastrukturę, w tym kontener IoC i Membus. Prawdopodobnie powinieneś mieć kilka takich testów, ale mimo to testy te są ważne. – Steven

+0

@Steven zgodził się! – GrahamMc

4

Jak Steven powiedział w swoich komentarzach, to brzmi jak piszesz test integracji, w tym przypadku ma to sens, aby użyć kontenera IoC.

Twój projekt testowy powinien mieć własny komponent Composition dla konfiguracji IoC. Możesz skonfigurować swój kontener IoC tak, aby zwrócił fałszywy obiekt dla AccountCommandHandlers. Twój test, zamiast sprawdzać członków boolean, może zamiast tego sprawdzić, czy Handle (CreateCommand) został wywołany co najmniej raz. Z Min, to będzie wyglądać mniej więcej tak:

mock.Verify(foo => foo.Handle(createAccountCommand), Times.AtLeastOnce()); 

Jeśli z jakiegoś powodu nie można użyć mock ramy z config IoC byłoby łatwo stworzyć własną klasę mock tym przypadku jedno badanie.

4

Jedno pytanie, które powinieneś sobie zadać to: Co właściwie chcę przetestować?

Twój zestaw testów nie musi koniecznie testować bibliotek innych firm. W powyższym przykładzie membus został zaprojektowany w celu dostarczania komunikatów do modułów obsługi, testy w tej bibliotece zapewniają, że tak właśnie jest.

Jeśli naprawdę chcesz, aby sprawdzić wiadomości -> IOC obsługi delegacja, w przypadku Membus można napisać autobusem -> adapter MKOl do testowania:

public class TestAdapter : IocAdapter 
{ 
    private readonly object[] _handlers; 

    public TestAdapter(params object[] handlers) 
    { 
     _handlers = handlers; 
    } 

    public IEnumerable<object> GetAllInstances(Type desiredType) 
    { 
     return _handlers; 
    } 
} 

a następnie używać go z wystąpień w ramach test.

 var bus = 
      BusSetup.StartWith<Conservative>() 
        .Apply<IoCSupport>(
         ioc => ioc.SetAdapter(new TestAdapter(new Handler<XYZ>())) 
        .SetHandlerInterface(typeof (IHandler<>))) 
        .Construct(); 

gdzie można przechowywać instancję Handler wokół, aby wykonać przeciwko niej twierdzenia.OTOH, jeśli ufasz bibliotece, że wykonasz swoją pracę, po prostu zaktualizujesz program obsługi i przetestujesz jego wewnętrzną logikę.

+0

Dobra racja, ale są inne rzeczy idące (serializacja) na inne testy (nie zawierałem, aby nie komplikować rzeczy), membus jest naprawdę wymieniony nie jako artefakt odchudzonego testu. membus jest świetny btw :) – g18c

Powiązane problemy