Jak testujesz funkcję prywatną w Angular 2?
class FooBar {
private _status: number;
constructor( private foo : Bar ) {
this.initFooBar();
}
private initFooBar(){
this.foo.bar( "data" );
this._status = this.fooo.foo();
}
public get status(){
return this._status;
}
}
Rozwiązanie, które znalazłem
Umieść sam kod testowy wewnątrz zamknięcia lub Dodaj kod wewnątrz zamknięcia, który przechowuje odwołania do zmiennych lokalnych na istniejących obiektach w zewnętrznym zakresie.
Następnie usuń kod testowy za pomocą narzędzia. http://philipwalton.com/articles/how-to-unit-test-private-functions-in-javascript/
Proszę zasugerować mi lepszy sposób rozwiązania tego problemu, jeśli już to zrobiłeś?
PS
Większość odpowiedzi na podobne pytania, takie jak to, nie daje rozwiązania problemu, dlatego zadaję to pytanie
Większość programistów twierdzi, że nie testujesz funkcji prywatnych, ale nie twierdzę, że są one złe lub słuszne, ale mój przypadek wymaga przetestowania funkcji prywatnych.
źródło
Odpowiedzi:
Jestem z tobą, mimo że dobrym celem jest „testowanie tylko publicznego interfejsu API”, zdarza się, że nie wydaje się to takie proste i masz wrażenie, że wybierasz między zagrożeniem dla interfejsu API lub testów jednostkowych. Wiesz już o tym, ponieważ dokładnie o to prosisz, więc nie będę w to wchodził. :)
W TypeScript odkryłem kilka sposobów uzyskiwania dostępu do prywatnych członków na potrzeby testów jednostkowych. Rozważ tę klasę:
Choć TS ogranicza dostęp do członków klasy za pomocą
private
,protected
,public
, skompilowany JS nie ma prywatnych członków, ponieważ nie jest to sprawa w JS. Jest używany wyłącznie w kompilatorze TS. W związku z tym:Możesz zapewnić
any
i uniknąć kompilatora przed ostrzeżeniem o ograniczeniach dostępu:Problem z tym podejściem polega na tym, że kompilator po prostu nie ma pojęcia, co robisz od razu
any
, więc nie otrzymujesz pożądanych błędów typu:To oczywiście utrudni refaktoryzację.
Możesz użyć tablicy access (
[]
), aby uzyskać dostęp do prywatnych członków:Choć wygląda na funky, TSC tak naprawdę sprawdza typy tak, jakbyś miał do nich bezpośredni dostęp:
Szczerze mówiąc, nie wiem, dlaczego to działa.Jest to najwyraźniej celowa „klapa ratunkowa” zapewniająca dostęp do członków prywatnych bez utraty bezpieczeństwa typu. Właśnie tego oczekuję od testów jednostkowych.Oto działający przykład na placu zabaw TypeScript .
Edycja dla TypeScript 2.6
Inną opcją, którą niektórzy lubią, jest
// @ts-ignore
( dodana w TS 2.6 ), która po prostu pomija wszystkie błędy w następującym wierszu:Problem polega na tym, że pomija wszystkie błędy w następującym wierszu:
Osobiście uważam
@ts-ignore
zapach kodu i, jak mówią doktorzy:źródło
as any
: tracisz wszystkie sprawdzanie typu.Możesz wywoływać metody prywatne . Jeśli wystąpił następujący błąd:
po prostu użyj
// @ts-ignore
:źródło
as any
, co utrata sprawdzania typu, w rzeczywistości tracisz sprawdzanie typu na całej linii.Ponieważ większość programistów nie zaleca testowania funkcji prywatnej , dlaczego nie przetestować?
Na przykład.
YourClass.ts
TestYourClass.spec.ts
Dzięki @Aaron, @Thierry Templier.
źródło
Nie pisz testów dla metod prywatnych. To pokonuje punkt testów jednostkowych.
Przykład
Test dla tej metody nie powinien wymagać zmiany, jeśli później implementacja ulegnie zmianie, ale
behaviour
publiczny interfejs API pozostanie taki sam.Nie upubliczniaj metod i właściwości tylko po to, aby je przetestować. Zazwyczaj oznacza to, że:
źródło
Istotą „nie testuj metod prywatnych” jest tak naprawdę przetestowanie klasy jak ktoś, kto jej używa .
Jeśli masz publiczny interfejs API z 5 metodami, każdy konsument z Twojej klasy może z nich korzystać, dlatego powinieneś je przetestować. Konsument nie powinien uzyskiwać dostępu do prywatnych metod / właściwości twojej klasy, co oznacza, że możesz zmieniać prywatnych członków, gdy publiczna funkcjonalność pozostanie taka sama.
Jeśli polegasz na wewnętrznej rozszerzalnej funkcjonalności, użyj
protected
zamiastprivate
.Zauważ, że
protected
wciąż jest publicznym interfejsem API (!) , Po prostu używany inaczej.Testuj chronione właściwości w taki sam sposób, w jaki konsument wykorzystałby je, poprzez podklasę:
źródło
To działało dla mnie:
Zamiast:
To:
źródło
Przepraszam za nekro w tym poście, ale czuję się zmuszony zastanowić się nad kilkoma rzeczami, które nie wydają się być poruszone.
Przede wszystkim - kiedy podczas testów jednostkowych potrzebujemy dostępu do prywatnych członków klasy, jest to na ogół duża, gruba czerwona flaga, którą wyłudziliśmy w naszym strategicznym lub taktycznym podejściu i nieumyślnie naruszyliśmy jedną zasadę odpowiedzialności, popychając zachowanie, do którego nie należy. Poczucie potrzeby dostępu do metod, które są tak naprawdę niczym więcej niż izolowanym podprogramem procedury budowlanej, jest jednym z najczęstszych tego zjawisk; jednak to trochę tak, jakby twój szef spodziewał się, że przyjdziesz do pracy gotowy, a także mając przewrotną potrzebę wiedzieć, przez jaką poranną rutynę przeszedłeś, aby dostać się do tego stanu ...
Innym najczęstszym przykładem tego zdarzenia jest próba przetestowania przysłowiowej „klasy boga”. Jest to szczególny rodzaj problemu sam w sobie, ale cierpi z powodu tego samego podstawowego problemu z koniecznością poznania szczegółowych szczegółów procedury - ale to zejście z tematu.
W tym konkretnym przykładzie skutecznie przypisaliśmy odpowiedzialność za pełną inicjalizację obiektu Bar do konstruktora klasy FooBar. W programowaniu obiektowym jednym z głównych założeń jest to, że konstruktor jest „święty” i powinien być chroniony przed nieprawidłowymi danymi, które unieważniłyby jego „wewnętrzny stan i pozostawiałyby go do upadku gdzieś indziej (w czym może być bardzo głęboka) rurociąg.)
Nie udało nam się tego tutaj zrobić, pozwalając obiektowi FooBar zaakceptować pasek, który nie jest gotowy w momencie budowy FooBar, i zrekompensowaliśmy go poprzez „hakowanie” obiektu FooBar, aby wziąć sprawy w swoje „własne” ręce.
Wynika to z nieprzestrzegania innej zasady programowania obiektowego (w przypadku Bar), która polega na tym, że stan obiektu powinien być w pełni zainicjowany i gotowy do obsługi wszelkich połączeń przychodzących do jego członków publicznych natychmiast po utworzeniu. Nie oznacza to natychmiast po wywołaniu konstruktora we wszystkich przypadkach. Jeśli masz obiekt, który ma wiele złożonych scenariuszy konstrukcyjnych, lepiej jest wystawić setery do opcjonalnych elementów składowych na obiekt, który jest implementowany zgodnie ze wzorcem projektowym tworzenia (fabryka, konstruktor itp.) W dowolnym te ostatnie przypadki,
W twoim przykładzie właściwość „status” paska nie wydaje się być w prawidłowym stanie, w którym FooBar może to zaakceptować - więc FooBar robi coś, aby rozwiązać ten problem.
Drugi problem, jaki widzę, polega na tym, że wydaje się, że próbujesz przetestować swój kod, zamiast ćwiczyć programowanie oparte na testach. To zdecydowanie moja własna opinia w tym momencie; ale tego typu testy są naprawdę anty-wzorcem. W końcu wpadasz w pułapkę uświadomienia sobie, że masz podstawowe problemy z projektowaniem, które uniemożliwiają testowanie kodu po fakcie, zamiast pisania potrzebnych testów, a następnie programowania do testów. Niezależnie od tego, jak podejdziesz do problemu, powinieneś skończyć z taką samą liczbą testów i linii kodu, jeśli naprawdę osiągnąłeś implementację SOLID. Więc - po co próbować odwracać swoją drogę do kodu, który można przetestować, skoro możesz po prostu rozwiązać ten problem na początku swoich prac programistycznych?
Gdybyś to zrobił, znacznie wcześniej zdałbyś sobie sprawę, że będziesz musiał napisać dość nieprzyjemny kod, aby przetestować swój projekt, i miałbyś możliwość wcześniejszego dostosowania swojego podejścia poprzez zmianę zachowania na implementacje, które są łatwe do przetestowania.
źródło
Zgadzam się z @toskv: Nie polecam tego robić :-)
Ale jeśli naprawdę chcesz przetestować swoją prywatną metodę, możesz mieć świadomość, że odpowiedni kod dla TypeScript odpowiada metodzie prototypu funkcji konstruktora. Oznacza to, że można go używać w czasie wykonywania (podczas gdy prawdopodobnie wystąpią pewne błędy kompilacji).
Na przykład:
zostaną przełożone na:
Zobacz ten fragment: https://plnkr.co/edit/calJCF?p=preview .
źródło
Jak już wielu z nich stwierdziło, tak bardzo, jak chcesz przetestować metody prywatne, nie powinieneś hakować kodu ani transpilatora, aby działał on dla Ciebie. Współczesny TypeScript odrzuci większość hacków, które ludzie udostępnili do tej pory.
Rozwiązanie
TLDR ; jeśli metoda powinna zostać przetestowana, należy oddzielić kod do klasy, dzięki czemu można udostępnić metodę do publicznego przetestowania.
Powodem, dla którego masz metodę prywatną, jest to, że funkcjonalność niekoniecznie musi być ujawniona przez tę klasę, a zatem jeśli funkcjonalność tam nie należy, powinna zostać oddzielona od swojej klasy.
Przykład
Natknąłem się na ten artykuł, który świetnie wyjaśnia, w jaki sposób należy radzić sobie z testowaniem prywatnych metod. Obejmuje nawet niektóre metody tutaj i dlaczego są to złe implementacje.
https://patrickdesjardins.com/blog/how-to-unit-test-private-method-in-typescript-part-2
Uwaga : ten kod jest usuwany z blogu, do którego prowadzi link powyżej (powielam się na wypadek zmiany treści za linkiem)
Przed Poźródło
wywołaj metodę prywatną za pomocą nawiasów kwadratowych
Plik Ts
plik spect.ts
źródło
Odpowiedź Aarona jest najlepsza i działa dla mnie :) Głosowałbym, ale niestety nie mogę (brak reputacji).
Muszę powiedzieć, że testowanie prywatnych metod jest jedynym sposobem na ich użycie i uzyskanie czystego kodu po drugiej stronie.
Na przykład:
„Sensowne jest nie testowanie wszystkich tych metod naraz, ponieważ musielibyśmy wyśmiewać te prywatne metody, których nie możemy wyszydzić, ponieważ nie mamy do nich dostępu. Oznacza to, że potrzebujemy dużo konfiguracji do testu jednostkowego, aby przetestować to jako całość.
To powiedziawszy, najlepszym sposobem przetestowania powyższej metody ze wszystkimi zależnościami jest test kompleksowy, ponieważ tutaj potrzebny jest test integracyjny, ale test E2E nie pomoże ci, jeśli ćwiczysz TDD (Test Driven Development), ale testowanie każda metoda będzie.
źródło
Ta droga, którą wybieram, jest tą, w której tworzę funkcje poza klasą i przypisuję funkcję do mojej prywatnej metody.
Teraz nie wiem, jaki rodzaj reguł OOP łamię, ale aby odpowiedzieć na pytanie, w ten sposób testuję prywatne metody. Z radością witam każdego, kto doradzi na ten temat.
źródło