Dependency injection w złożonym projekcie

0

Rozumiem ten wzorzec projektowy i potrafię go zastosować do pojedynczej klasy, mam problem w jaki sposób go wykorzystać w bardziej złożonych projektach.

Mam hierarchie wielu klas w projekcie, ostatnia klasa odpowiada za zapis do pliku lub komunikację sieciową. Łatwo sobie przygotowuje mocka dla klasy nadrzędnej i omijam kosztowne operacje IO. Pytanie jak to mockować od pierwszej klasy? Bo mało eleganckie w złożonym projekcie trzymać wszystkie zależności w pierwszej klasie, aby budować obiekty w dół.

W jaki sposób Wy to robicie w swoich projektach?

A(main)
B <- chce napisać unit testy dla B, ale nie chce robić operacji IO na prawdziwym sprzęcie
C
..
D
E (mock_f) -> mock_f->write
F <- jakieś operacje IO

2

Jeśli testujesz jednostkowo B, to w teście nie powinieneś mu dawać C, D, E ani F. Powinieneś napisać testową implementację C, albo przekazać ją jakoś inaczej (mockito)?

Jeśli natomiast chcesz stworzyć całą hierarchię klas w teście/ albo część hierarchii to to już nie jest test jednostkowy tylko interakcyjny/integracyjny. Jeśli chcesz taki napisać, to albo musisz mieć schowane budowanie w fabryce albo w builderze, albo mieć drzewko

new A(new B(new C(new D(new E())))

w teście. Ja tak robię w mojej libce T-Regx, możesz przejrzeć parę testów, jeśli chcesz zobaczyć jak to u mnie wygląda:

W skrócie, fajnie by było gdyby tworzenie instancji w teście, było podobne do tego jak je tworzysz w kodzie.

0

No powiedzmy chce napisać unit test dla B i wiem, że B gdzieś dalej używa C, C uzywa D a w D jest zapis do pliku.

Jak testuje tylko C to moge uzyć właśnie DI i przekazac mocka zamiast D, wtedy nie bede pisal do prawdziwego pliku. Wszystko elegancko.

Jak testować B i mockować to co się dzieje gdzieś głębiej w D przez C? Bo robi mi się taka drabinka, że robie interfejs w B, który mi pozwoli przekazać mocka do C a potem do D. Jak w projekcie mamy wiele takich poziomów to robi się piekło zależności :) Klasa B nie musi nic wiedzieć o D, ale przez to, że chcemy mockować to musimy już od niej przekazać mocki w dół.

1

Z głowy na szybko, czemu nie +- tak:


template <typename C>
class B
{
public:
  B(const C& c);
 //ciach;
};
using BProd = B<CProd>;
using BTest= B<CMocked>;

EDIT: opcja nr 2, ale dla mnie to trochę hardkor, niemniej widziałem to stosowane w praktyce i działało: podmiana mock/prod na etapie linkowania.

1
gemini napisał(a):

No powiedzmy chce napisać unit test dla B i wiem, że B gdzieś dalej używa C, C uzywa D a w D jest zapis do pliku.

Jeśli chcesz testować jednostkowo B, to nie powinno Cię interesować nic z tego co robi C pod spodem. Powinno Cię interesować tylko interfejs C, ale jego implementacja (czyli to że korzysta z D) na pewno nie.

Jak testuje tylko C to moge uzyć właśnie DI i przekazac mocka zamiast D, wtedy nie bede pisal do prawdziwego pliku. Wszystko elegancko.

No tak

Jak testować B i mockować to co się dzieje gdzieś głębiej w D przez C?

Nie mockować D. Mockować tylko C.

Bo robi mi się taka drabinka, że robie interfejs w B, który mi pozwoli przekazać mocka do C a potem do D.

Jak w projekcie mamy wiele takich poziomów to robi się piekło zależności :)

No nie sądzę. Piekło zależności by było gdyby B zależał od C, D, E i F. Ale zależy tylko od C :) To od czego zależy C z punktu widzenia B to szczegół implementacyjny, którym nie musisz się martwić.

Klasa B nie musi nic wiedzieć o D, ale przez to, że chcemy mockować to musimy już od niej przekazać mocki w dół.

W teście? Nie, bo już C jest mockiem, i nie dostanie żadnych innych mocków.
W logice? Tak, ale przekazanie zależności w dół to właśnie duch dependency injection.

1

Ja osobiście polecam testować funkcje systemu a nie klasy. Problem sam znika od ręki. Polecam w ogóle nie myśleć o tym że masz klasę B która korzysta z klasy C, a tamta korzysta z D... to są szczegóły implementacyjne. A co jak nagle postanowisz ze dzielisz D na E i F? Czemu chciałbyś żeby twoje testy się przez to popsuły? :)

0
Shalom napisał(a):

Ja osobiście polecam testować funkcje systemu a nie klasy. Problem sam znika od ręki. Polecam w ogóle nie myśleć o tym że masz klasę B która korzysta z klasy C, a tamta korzysta z D... to są szczegóły implementacyjne. A co jak nagle postanowisz ze dzielisz D na E i F? Czemu chciałbyś żeby twoje testy się przez to popsuły? :)

Testowanie jednostkowe i cało-systemowe oba mają swoje wady i zalety. Kiedyś napisałem tabelkę chyba 30 zalet i wad takich rozwiązań. @Shalom Mogę podrzucić, gdybyś chciał.

1 użytkowników online, w tym zalogowanych: 0, gości: 1