Czy wolisz, aby prywatne rzeczy były wewnętrzne / publiczne do testów, czy użyć jakiegoś hacka, takiego jak PrivateObject?

26

Jestem całkiem początkującym w testowaniu kodu, a wcześniej byłem assertdziwką. Jedną z rzeczy, która mnie martwi w testach jednostkowych, jest to, że często wymaga to od ciebie public(lub przynajmniej internal) wypełnienia pól, które byłyby privateinaczej, usunięcia readonlyich, zamiast tego stworzenia privatemetod protected virtualitp.

Niedawno odkryłem, że można tego uniknąć, używając rzeczy takich jak klasa PrivateObject, aby uzyskać dostęp do czegokolwiek w obiekcie poprzez odbicie. Ale to sprawia, że ​​twoje testy są mniej łatwe w utrzymaniu (rzeczy zawiedzie podczas wykonywania niż kompilacji czasu, zostanie zepsute przez zwykłą zmianę nazwy, trudniej jest debugować ...). Jaka jest twoja opinia na ten temat ? Jakie są najlepsze praktyki w testowaniu jednostkowym dotyczące ograniczenia dostępu?

edytuj: rozważ na przykład, że masz klasę z pamięcią podręczną w pliku na dysku, aw testach zamiast tego chcesz zapisać w pamięci.

Zonko
źródło
2
Pythonowie mają tylko publiczne. Są szczęśliwi. Po co się martwić? Dlaczego nie upublicznić wszystkiego?
S.Lott,
12
Ponieważ jest to dalekie od najlepszych praktyk w większości najpopularniejszych języków. Prawdziwą odpowiedzią jest używanie dobrych interfejsów i takich, aby można było używać próbnych obiektów do przeprowadzania testów.
Rig
2
@Rig: Python nie jest najlepszym językiem? Ciekawy.
S.Lott,
7
„jest daleki od najlepszych praktyk w większości najpopularniejszych języków” nie oznacza logicznie (ani nawet nie wspomina o tym) „Python nie jest najlepszym językiem”.
Mike Nakis,
1
Dokładnie, jest to najlepszy język, ale większość najpopularniejszych języków nie jest w Pythonie i popiera ustawienie odpowiedniego zakresu. Podtrzymuję moje oświadczenie. Istnieją wzorce zaprojektowane do tworzenia wysoce testowalnego oprogramowania, przy jednoczesnym zapewnieniu, że zmienne zachowują zakres. Nawet programiści Pythona mają tendencję do emulacji zakresu za pomocą prefiksów z tego, co widziałem.
Rig

Odpowiedzi:

36

Nigdy nie powinieneś

twórz public(a przynajmniej internal) pola, które byłyby privateinaczej, aby readonlyje usunąć , zamiast tego twórz privatemetodyprotected virtual

Zwłaszcza nieprzyłączenie pola może spowodować, że niezmienny obiekt będzie podlegał modyfikacji, a to byłaby katastrofa.

Twoje testy powinny traktować twoje obiekty jako czarne skrzynki ( Wikipedia ), co oznacza, że ​​powinny one dotyczyć wyłącznie publicznego interfejsu obiektów, a nie szczegółów ich implementacji.

Jeśli obiektu nie można wystarczająco przetestować przy użyciu jego publicznego interfejsu, musisz znaleźć sposoby na zapewnienie formalnych i użytecznych rozszerzeń jego interfejsu, które ułatwiłyby testowanie. Na przykład system rejestracji wykorzystujący tylko parę metod rejestracji / wyrejestrowania mógłby skorzystać z metody IsRegistered, nawet jeśli nie jest to konieczne dla danego wniosku; nadal jest to formalne i użyteczne rozszerzenie interfejsu, a nawiasem mówiąc, będzie mogło pomieścić testy.

Ważne jest to, że zmiana implementacji obiektu nie powinna wymagać zmiany testu jednostkowego. Teoretycznie powinieneś być w stanie napisać test jednostkowy raz, a następnie poprosić wielu programistów o zakodowanie wielu całkowicie różnych implementacji testowanego obiektu do pracy z testem jednostkowym.

Mike Nakis
źródło
2
Dokładnie! Jeśli nie możesz przetestować urządzenia bez odbicia lub włamań, prawdopodobnie masz błąd w interfejsie API lub projekcie.
Falcon,
Czy mówisz, że tworzenie privatemetod protected virtualpomagających w kpinach w testach jest uważane za złą praktykę?
Robula
1
@Robula W mojej książce tak. Nie piszę nawet testów w tym samym pakiecie co kod produkcyjny, ponieważ nie mam sensu uzyskiwać dostępu do członków niepublicznych. Ja też nie używam kpiny. Oczywiście, jeśli musisz użyć próbnych, będziesz musiał zrobić wszystko, aby ułatwić wyśmiewanie, więc chroniony wirtualny może być praktycznym kompromisem w wielu sytuacjach. Ale idealnie, projekt nie potrzebuje prób, tylko alternatywne implementacje, a idealnie testowanie to testowanie w czarnej skrzynce, a nie w białe pudełko, więc rzeczy są testowane w integracji, bez prób.
Mike Nakis
13

W C # możesz użyć InternalsVisibleToAttribute aby umożliwić zestawowi testowemu zobaczyć internalklasy w zestawie, który testujesz. Wygląda na to, że już to wiesz.

W większości przypadków jestem zainteresowany testowaniem publicznego interfejsu API mojego zestawu. W przypadku, gdy chcę wykonać testy białych jednostek dla niektórych jednostek, przenoszę kod dointernal klasy i ustawiam publicmetodę tej klasy. Następnie mogę zakodować test jednostkowy dla tej klasy.

To dobrze nadaje się do wstrzykiwania zależności i wzorca strategii. Możesz wyodrębnić metodę do interfejsu, wstrzyknąć interfejs do konstruktora obiektu nadrzędnego i po prostu przetestować, czy rodzic odpowiednio deleguje. Następnie możesz przetestować, czy obiekt potomny zachowuje się odpowiednio. Dzięki temu wszystko jest bardziej modułowe.

Scott Whitlock
źródło
8

Z mojego doświadczenia najlepiej nie wystawiać wewnętrznych elementów twojej klasy specjalnie na test. Chociaż może się to wydawać, aby test był łatwiejszy do napisania. Test jest pierwszym klientem twojej klasy, więc jeśli trudno jest przetestować klasę za pomocą publicznego interfejsu, prawdopodobnie trudno będzie użyć twojej klasy również w innych miejscach.

Jak łatwo jest przetestować klasę za pomocą publicznego interfejsu API, jest informacja zwrotna na temat tego, jak łatwa będzie klasa. Pomyśl o testach częściowo jako o prototypie użycia twojej klasy.

Zastanów się, dlaczego twój test musi wykryć coś o klasie, która nie jest dostępna za pośrednictwem publicznego interfejsu API. Być może twoja klasa robi za dużo i zamiast tego musi zostać podzielona na grupę klas współpracujących? Następnie możesz wyśmiewać niektóre ze współpracujących klas, aby wyczuć, co robi testowana klasa.

Spróbuj zastosować zasadę inwersji zależności i wprowadzić interfejsy między klasami, które możesz wyśmiewać w testach. Możesz podać współpracowników do testu, stosując odwrócenie kontroli .

Zastanów się także nad zastosowaniem zasady „mów nie pytaj”, aby ustrukturyzować interfejs swojej klasy w solidny, luźno powiązany sposób, w którym odpowiednie informacje są ujawniane, a reszta jest ukryta.

Flamingpenguin
źródło
6

Osobiście wolę zostawić kod, który testuję tak czysto, jak to tylko możliwe, a jeśli kod testowy wymaga włamań (oraz w paskudnych wskaźnikach w C ++ itp.) Jestem szczęśliwy, że mogę to zrobić.

Mrówka
źródło
1
Zgadzam się, tak to się robi. Trzymaj brzydotę w kodzie testowym, gdzie nic nie może zranić.
Alan Delimon,
@AlanDelimon Czy to nie zaszkodzi umysłowi kolejnych programistów serwisowych?
flamingpenguin
1
@flamingpenguin brzydki kod może zaszkodzić programistom w dowolnym miejscu; ale brzydki kod testowy prawdopodobnie wyrządzi mniej szkody, ponieważ wpłynie tylko na osoby modyfikujące klasę, a nie także na osoby, które po prostu ją wykorzystują.
Dan Neely,
2
@flamingpenguin Może, ale jeśli musisz gdzieś umieścić brzydki kod, równie dobrze może to być test jednostkowy, w którym mniej prawdopodobne jest, że będzie wymagał modyfikacji i będzie częścią aplikacji.
Alan Delimon,
5

Widoczność kodu nie ma nic wspólnego z testami jednostkowymi!

Czynisz swoje metody prywatnymi, a nie po to, by ukrywać je przed testowaniem, i nie upubliczniasz ich do testowania, prawda? To wasza implementacja jest tutaj niezbędna, a nie testy - te serwery są dowodem twojego kodu, ale nie są twoim kodem .

Istnieje wiele sposobów, aby umożliwić dostęp do ukrytych (prywatnych lub wewnętrznych) metod i właściwości. Wiele frameworków testowych i IDE (na przykład Visual Studio) obsługuje cię tutaj, generując wszystko, co jest potrzebne do uzyskania dostępu, musisz tylko stworzyć swoje przypadki testowe. Dlaczego więc się martwić? Lepiej utrzymuj kod w czystości i porządku.

Alexander Galkin
źródło