Buduję bibliotekę klas, która będzie miała kilka publicznych i prywatnych metod. Chcę mieć możliwość testowania jednostkowego metod prywatnych (głównie podczas opracowywania, ale może to również być przydatne do przyszłego refaktoryzacji).
Jaki jest właściwy sposób to zrobić?
.net
unit-testing
tdd
private
Eric Labashosky
źródło
źródło
pre-historic
jeśli chodzi o lata internetowe, ale testowanie jednostkowe metod prywatnych jest teraz zarówno łatwe, jak i bezpośrednie, a Visual Studio produkuje w razie potrzeby niezbędne klasy akcesoriów. wstępne wypełnianie logiki testów fragmentami cholernie zbliżonymi do tego, czego można chcieć od prostych testów funkcjonalnych. Zobacz np. msdn.microsoft.com/en-us/library/ms184807%28VS.90%29.aspxOdpowiedzi:
Jeśli używasz .net, powinieneś użyć InternalsVisibleToAttribute .
źródło
#if DEBUG
tegoInternalsVisibleTo
atrybutu, aby nie dotyczył kodu wersji?#if RELEASE_TEST
wokółInternalsVisibleTo
jak Mike sugeruje, i zrobić kopię konfiguracji kompilacji uwalniania, który definiujeRELEASE_TEST
. Możesz przetestować swój kod wersji z optymalizacjami, ale kiedy faktycznie budujesz dla wydania, twoje testy zostaną pominięte.Jeśli chcesz przetestować metodą prywatną metodę prywatną, coś może być nie tak. Testy jednostkowe (ogólnie) mają na celu przetestowanie interfejsu klasy, co oznacza jej publiczne (i chronione) metody. Możesz oczywiście „zhakować” rozwiązanie tego problemu (nawet jeśli tylko upublicznisz metody), ale możesz też rozważyć:
źródło
Testowanie metod prywatnych może nie być przydatne. Jednak czasami lubię też nazywać metody prywatne metodami testowymi. Przez większość czasu, aby zapobiec duplikacji kodu do generowania danych testowych ...
Microsoft udostępnia w tym celu dwa mechanizmy:
Akcesoria
Jednak mechanizm ten jest czasem nieco trudny do rozwiązania, jeśli chodzi o zmiany interfejsu oryginalnej klasy. Tak więc przez większość czasu unikam tego.
Klasa PrivateObject Innym sposobem jest użycie Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject
źródło
Nie zgadzam się z filozofią „powinieneś być zainteresowany jedynie testowaniem interfejsu zewnętrznego”. To trochę tak, jakby powiedzieć, że warsztat samochodowy powinien mieć tylko testy, aby sprawdzić, czy koła się obracają. Tak, ostatecznie interesuje mnie zachowanie zewnętrzne, ale podoba mi się to, że moje własne, wewnętrzne testy są bardziej szczegółowe i do rzeczy. Tak, jeśli dokonam refaktoryzacji, być może będę musiał zmienić niektóre testy, ale chyba że jest to ogromny refaktor, będę musiał zmienić tylko kilka, a fakt, że inne (niezmienione) testy wewnętrzne nadal działają, jest doskonałym wskaźnikiem, że refaktoryzacja zakończyła się powodzeniem.
Możesz spróbować objąć wszystkie przypadki wewnętrzne przy użyciu tylko interfejsu publicznego i teoretycznie możliwe jest przetestowanie każdej metody wewnętrznej (lub przynajmniej każdej, która ma znaczenie) całkowicie za pomocą interfejsu publicznego, ale może być konieczne stanie się na głowie, aby osiągnąć to i połączenie między przypadkami testowymi uruchamianymi przez interfejs publiczny a wewnętrzną częścią rozwiązania, które mają przetestować, może być trudne lub niemożliwe do rozpoznania. Po wskazaniu, indywidualne testy, które gwarantują, że wewnętrzna maszyna działa poprawnie, są warte drobnych zmian testowych, które pojawiają się przy refaktoryzacji - przynajmniej takie było moje doświadczenie. Jeśli musisz wprowadzić ogromne zmiany w swoich testach dla każdego refaktoryzacji, to może nie ma to sensu, ale w takim przypadku może powinieneś całkowicie przemyśleć swój projekt.
źródło
FooService
do zrobieniaX
, powinieneś się tylko martwić, że tak naprawdę dzieje się naX
żądanie. Jak to nie powinno mieć znaczenia. Jeśli występują problemy w klasie, których nie można rozpoznać za pośrednictwem interfejsu (mało prawdopodobne), nadal jest to poprawneFooService
. Jeśli jest to problem, który jest widoczny poprzez interfejs, test na członków publicznych powinien go wykryć. Chodzi o to, że dopóki koło obraca się prawidłowo, może być używane jako koło.PrivMethod
, test, naPubMethod
który połączeniaPrivMethod
powinny go ujawnić? Co się stanie, gdy zmienisz swójSimpleSmtpService
wGmailService
? Nagle twoje prywatne testy wskazują na kod, który już nie istnieje lub może działa inaczej i nie powiedzie się, nawet jeśli aplikacja może działać idealnie zgodnie z przeznaczeniem. Jeśli istnieje skomplikowane przetwarzanie, które dotyczyłoby obu nadawców e-maili, być może powinno być w takim,EmailProcessor
które może być używane przez oba i testowane osobno?W rzadkich przypadkach chciałem przetestować funkcje prywatne, zwykle zmodyfikowałem je, aby były chronione, i napisałem podklasę z funkcją publicznego pakowania.
Klasa:
Podklasa do testowania:
źródło
Myślę, że bardziej fundamentalnym pytaniem powinno być to, dlaczego próbujesz w pierwszej kolejności przetestować metodę prywatną. Jest to zapach kodu, który próbujesz przetestować metodę prywatną za pomocą publicznego interfejsu tej klasy, podczas gdy ta metoda jest prywatna z jakiegoś powodu, ponieważ jest szczegółem implementacji. Należy się tylko przejmować zachowaniem interfejsu publicznego, a nie jego implementacją pod przykryciem.
Jeśli chcę przetestować zachowanie metody prywatnej, używając wspólnych refaktoryzacji, mogę wyodrębnić jej kod do innej klasy (być może z widocznością na poziomie pakietu, więc upewnij się, że nie jest to część publicznego API). Następnie mogę przetestować jego zachowanie w izolacji.
Produkt refaktoryzacji oznacza, że metoda prywatna jest teraz oddzielną klasą, która stała się współpracownikiem oryginalnej klasy. Jego zachowanie zostanie dobrze poznane dzięki własnym testom jednostkowym.
Następnie mogę kpić z jego zachowania, gdy próbuję przetestować oryginalną klasę, dzięki czemu mogę skoncentrować się na przetestowaniu zachowania publicznego interfejsu tej klasy, zamiast testować kombinatoryczną eksplozję publicznego interfejsu i zachowanie wszystkich jego prywatnych metod .
Widzę to analogicznie do prowadzenia samochodu. Kiedy prowadzę samochód, nie jeżdżę z podniesioną maską, więc widzę, że silnik działa. Polegam na interfejsie, który zapewnia samochód, a mianowicie na obrotomierzu i prędkościomierzu, aby wiedzieć, że silnik działa. Polegam na tym, że samochód faktycznie porusza się po naciśnięciu pedału gazu. Jeśli chcę przetestować silnik, mogę to sprawdzić osobno. :RE
Oczywiście bezpośrednie testowanie metod prywatnych może być ostatecznością, jeśli masz starszą aplikację, ale wolałbym, aby starszy kod został ponownie przekształcony, aby umożliwić lepsze testowanie. Michael Feathers napisał świetną książkę na ten właśnie temat. http://www.amazon.co.uk/Working-Effectively-Legacy-Robert-Martin/dp/0131177052
źródło
Typy prywatne, wewnętrzne i prywatni członkowie są z jakiegoś powodu i często nie chcesz z nimi zadzierać bezpośrednio. A jeśli to zrobisz, są szanse, że później zepsujesz, ponieważ nie ma gwarancji, że faceci, którzy utworzyli te zespoły, zachowają prywatne / wewnętrzne implementacje jako takie.
Ale czasami, gdy robię jakieś włamania / eksploracje zestawień skompilowanych lub zestawów stron trzecich, sam chciałem zainicjować klasę prywatną lub klasę z konstruktorem prywatnym lub wewnętrznym. Lub czasami, gdy mam do czynienia ze wstępnie skompilowanymi starszymi bibliotekami, których nie mogę zmienić - w końcu piszę testy przeciwko prywatnej metodzie.
Tak narodził się AccessPrivateWrapper - http://amazedsaint.blogspot.com/2010/05/accessprivatewrapper-c-40-dynamic.html - jest to klasa szybkiego która ułatwi zadanie dzięki dynamicznym funkcjom i refleksji C # 4.0.
Możesz tworzyć typy wewnętrzne / prywatne, takie jak
źródło
Możesz przetestować prywatną metodę na dwa sposoby
możesz utworzyć instancję
PrivateObject
klasy, składnia jest następującaMożesz użyć odbicia.
źródło
PrivateClass
pierwszego i użyć go. stackoverflow.com/questions/9122708/…Użyłem również metody InternalsVisibleToAttribute. Warto również wspomnieć, że jeśli czujesz się niekomfortowo, wprowadzając wewnętrzne metody prywatne, aby to osiągnąć, być może nie powinny one być przedmiotem bezpośrednich testów jednostkowych.
W końcu testujesz zachowanie swojej klasy, a nie jej konkretną implementację - możesz zmienić drugą bez zmiany poprzedniej, a twoje testy powinny zostać zaliczone.
źródło
Istnieją 2 rodzaje metod prywatnych. Statyczne metody prywatne i niepasujące metody prywatne (metody instancji). Poniższe 2 artykuły wyjaśniają, w jaki sposób testować jednostkowe metody prywatne na przykładach.
źródło
MS Test ma wbudowaną ciekawą funkcję, która udostępnia prywatnych członków i metody w projekcie, tworząc plik o nazwie VSCodeGenAccessors
Z klasami wywodzącymi się z BaseAccessor
Jak na przykład
źródło
Na CodeProject znajduje się artykuł, który krótko omawia zalety i wady testowania metod prywatnych. Następnie udostępnia kod refleksyjny umożliwiający dostęp do metod prywatnych (podobny do kodu podanego powyżej przez Marcusa). Jedyny problem, jaki znalazłem w próbce, to to, że kod nie uwzględnia przeciążonych metod.
Artykuł znajdziesz tutaj:
http://www.codeproject.com/KB/cs/testnonpublicmembers.aspx
źródło
Zadeklaruj je
internal
, a następnie użyj,InternalsVisibleToAttribute
aby pozwolić zespołowi testowemu urządzenia je zobaczyć.źródło
Nie używam dyrektyw kompilatora, ponieważ szybko zaśmiecają wszystko. Jednym ze sposobów na złagodzenie tego, jeśli naprawdę ich potrzebujesz, jest umieszczenie ich w częściowej klasie, a twoja kompilacja zignoruje ten plik .cs podczas tworzenia wersji produkcyjnej.
źródło
Nie powinieneś testować prywatnych metod swojego kodu. Powinieneś testować „interfejs publiczny” lub API, publiczne rzeczy swoich klas. Interfejs API to wszystkie metody publiczne, które udostępniasz zewnętrznym rozmówcom.
Powodem jest to, że gdy zaczniesz testować prywatne metody i elementy wewnętrzne swojej klasy, łączysz implementację swojej klasy (rzeczy prywatne) z testami. Oznacza to, że kiedy zdecydujesz się zmienić szczegóły implementacji, będziesz również musiał zmienić swoje testy.
Z tego powodu należy unikać używania InternalsVisibleToAtrribute.
Oto świetna rozmowa Iana Coopera na ten temat: Ian Cooper: TDD, gdzie wszystko poszło nie tak
źródło
Czasami dobrze jest przetestować prywatne deklaracje. Zasadniczo kompilator ma tylko jedną metodę publiczną: Compile (string outputFileName, params string [] sourceSFileNames). Jestem pewien, że rozumiesz, że trudno byłoby przetestować taką metodę bez testowania każdej „ukrytej” deklaracji!
Dlatego stworzyliśmy Visual T #: aby ułatwić testy. Jest to darmowy język programowania .NET (zgodny z C # v2.0).
Dodaliśmy operatora „.-”. Zachowuje się jak „”. operator, z wyjątkiem tego, że możesz również uzyskać dostęp do dowolnej ukrytej deklaracji z testów bez zmiany czegokolwiek w testowanym projekcie.
Zajrzyj na naszą stronę internetową: pobierz ją za darmo .
źródło
Dziwię się, że nikt tego jeszcze nie powiedział, ale rozwiązaniem, które zastosowałem, jest stworzenie statycznej metody wewnątrz klasy w celu przetestowania się. Daje to dostęp do wszystkiego, co publiczne i prywatne do przetestowania.
Ponadto w języku skryptowym (z funkcjami OO, takimi jak Python, Ruby i PHP), możesz wykonać test pliku po uruchomieniu. Fajny szybki sposób upewnienia się, że twoje zmiany niczego nie zepsują. To oczywiście tworzy skalowalne rozwiązanie do testowania wszystkich twoich klas: po prostu uruchom je wszystkie. (możesz to również zrobić w innych językach z pustą nazwą główną, która zawsze uruchamia również swoje testy).
źródło
Chcę utworzyć tutaj czytelny przykład kodu, którego można użyć w dowolnej klasie, w której chcesz przetestować metodę prywatną.
W klasie przypadków testowych po prostu uwzględnij te metody, a następnie zastosuj je, jak wskazano.
$ this -> _ callMethod ('_ someFunctionName', tablica (param1, param2, param3));
Wystarczy wydać parametry w kolejności, w jakiej pojawiają się w oryginalnej funkcji prywatnej
źródło
Dla każdego, kto chce uruchamiać prywatne metody bez zgiełku i bałaganu. Działa to z dowolną strukturą testowania jednostkowego wykorzystującą tylko dobre stare odbicie.
Następnie w swoich rzeczywistych testach możesz zrobić coś takiego:
źródło
MbUnit ma ładne opakowanie dla tego, zwanego Odbłyśnikiem.
Możesz także ustawić i uzyskać wartości z właściwości
W odniesieniu do „testu prywatnego” zgadzam się, że… w idealnym świecie. nie ma sensu przeprowadzać prywatnych testów jednostkowych. Ale w prawdziwym świecie możesz chcieć napisać prywatne testy zamiast refaktoryzować kod.
źródło
Reflector
został zastąpiony przez potężniejszyMirror
w Gallio / MbUnit v3.2. ( gallio.org/wiki/doku.php?id=mbunit:mirror )Oto dobry artykuł na temat testowania jednostkowego metod prywatnych. Ale nie jestem pewien, co jest lepsze, aby twoja aplikacja została zaprojektowana specjalnie do testowania (to jak tworzenie testów tylko do testowania) lub użyć refleksu do testowania. Jestem pewien, że większość z nas wybierze drugi sposób.
źródło
Moim zdaniem powinieneś przetestować tylko publiczny interfejs API swojej klasy.
Upublicznienie metody w celu jej jednostkowego przetestowania powoduje przerwanie enkapsulacji ujawniając szczegóły implementacji.
Dobry publiczny interfejs API rozwiązuje bezpośredni cel kodu klienta i rozwiązuje go całkowicie.
źródło
Używam PrivateObject klasę. Ale jak wspomniano wcześniej, lepiej unikać testowania metod prywatnych.
źródło
„CC” to kompilator wiersza poleceń w systemie, którego używam.
-Dfoo=bar
robi równowartość#define foo bar
. Tak więc ta opcja kompilacji skutecznie zmienia wszystkie prywatne rzeczy na publiczne.źródło
Oto przykład, najpierw sygnatura metody:
Oto test:
źródło
Sposobem na to jest posiadanie metody
protected
i napisanie testowego urządzenia, które dziedziczy testowaną klasę. W ten sposób nie zmieniasz metodypublic
, ale włączasz testowanie.źródło
1) Jeśli masz starszy kod, jedynym sposobem na przetestowanie metod prywatnych jest refleksja.
2) Jeśli jest to nowy kod, masz następujące opcje:
Wolę metodę adnotacji, najprostszą i najmniej skomplikowaną. Jedyną kwestią jest to, że zwiększyliśmy widoczność, która moim zdaniem nie stanowi dużego problemu. Zawsze powinniśmy kodować do interfejsu, więc jeśli mamy interfejs MyService i implementację MyServiceImpl, wówczas możemy mieć odpowiednie klasy testowe, czyli MyServiceTest (metody interfejsu testowego) i MyServiceImplTest (testuj metody prywatne). Wszyscy klienci i tak powinni korzystać z interfejsu, więc nawet jeśli widoczność metody prywatnej została zwiększona, nie powinno to mieć znaczenia.
źródło
Możesz również zadeklarować go jako publiczny lub wewnętrzny (z InternalsVisibleToAttribute) podczas budowania w trybie debugowania:
Nadyma kod, ale będzie
private
w kompilacji wydania.źródło
Możesz wygenerować metodę testową dla metody prywatnej z Visual Studio 2008. Po utworzeniu testu jednostkowego dla metody prywatnej do projektu testowego dodawany jest folder Odwołania testowe, a do tego folderu dodawany jest moduł dostępu. Accessor jest również określany w logice metody testu jednostkowego. Ten akcesor umożliwia testowi jednostkowemu wywoływanie metod prywatnych w testowanym kodzie. Po szczegóły zajrzyj
http://msdn.microsoft.com/en-us/library/bb385974.aspx
źródło
Zauważ również, że InternalsVisibleToAtrribute ma wymaganie, aby twój zespół był silnie nazwany , co stwarza własny zestaw problemów, jeśli pracujesz w rozwiązaniu, które wcześniej nie miało tego wymagania. Używam akcesora do testowania metod prywatnych. Zobacz to pytanie jako przykład tego.
źródło
InternalsVisibleToAttribute
nie , nie wymaga się, że zespoły silnie nazwane. Obecnie używam go w projekcie, w którym tak nie jest.