Dyskusja na temat Czy testujesz metodę prywatną ma charakter informacyjny.
Zdecydowałem, że na niektórych zajęciach chcę mieć metody chronione, ale je przetestuj. Niektóre z tych metod są statyczne i krótkie. Ponieważ większość publicznych metod korzysta z nich, prawdopodobnie będę mógł później bezpiecznie usunąć testy. Ale aby zacząć od podejścia TDD i uniknąć debugowania, naprawdę chcę je przetestować.
Myślałem o następujących kwestiach:
- Metoda Obiekt wskazana w odpowiedzi wydaje się być nadmierną umiejętnością w tym zakresie.
- Zacznij od metod publicznych, a gdy kod zostanie objęty testami wyższego poziomu, włącz ich ochronę i usuń testy.
- Dziedzicz klasę z testowalnym interfejsem upubliczniającym chronione metody
Jaka jest najlepsza praktyka? Czy jest coś jeszcze?
Wygląda na to, że JUnit automatycznie zmienia chronione metody na publiczne, ale nie przyjrzałem się temu głębiej. PHP nie pozwala na to poprzez refleksję .
php
unit-testing
phpunit
GrGr
źródło
źródło
Odpowiedzi:
Jeśli używasz PHP5 (> = 5.3.2) z PHPUnit, możesz przetestować swoje prywatne i chronione metody, używając refleksji, aby ustawić je jako publiczne przed uruchomieniem testów:
źródło
protected
metoda jest również częścią publicznego interfejsu API, ponieważ każda klasa strony trzeciej może ją rozszerzyć i używać bez żadnej magii. Myślę więc, że tylkoprivate
metody należą do kategorii metod, które nie mogą być bezpośrednio testowane.protected
ipublic
powinny być bezpośrednio testowane.Wygląda na to, że już jesteś świadomy, ale i tak to powtórzę; To zły znak, jeśli musisz przetestować chronione metody. Celem testu jednostkowego jest przetestowanie interfejsu klasy, a chronione metody są szczegółami implementacji. To powiedziawszy, są przypadki, w których ma to sens. Jeśli używasz dziedziczenia, możesz zobaczyć nadklasę jako interfejs dla podklasy. Więc tutaj musiałbyś przetestować metodę chronioną (ale nigdy prywatną ). Rozwiązaniem tego jest utworzenie podklasy do celów testowych i wykorzystanie jej do ujawnienia metod. Na przykład.:
Pamiętaj, że zawsze możesz zastąpić dziedziczenie kompozycją. Podczas testowania kodu zwykle łatwiej jest radzić sobie z kodem korzystającym z tego wzorca, więc warto rozważyć tę opcję.
źródło
teastburn ma właściwe podejście. Jeszcze prostsze jest bezpośrednie wywołanie metody i zwrócenie odpowiedzi:
Możesz to nazwać w testach:
źródło
Chciałbym zaproponować niewielką odmianę metody getMethod () zdefiniowanej w odpowiedzi uckelman .
Ta wersja zmienia getMethod () poprzez usunięcie zakodowanych wartości i uproszczenie użytkowania. Polecam dodanie go do swojej klasy PHPUnitUtil, jak w poniższym przykładzie, lub do klasy rozszerzającej PHPUnit_Framework_TestCase (lub, jak sądzę, globalnie do pliku PHPUnitUtil).
Ponieważ i tak tworzona jest instancja MyClass, a ReflectionClass może pobrać ciąg znaków lub obiekt ...
Stworzyłem również funkcję aliasu getProtectedMethod (), aby wyraźnie określić, czego się oczekuje, ale to zależy od ciebie.
Twoje zdrowie!
źródło
Myślę, że troelskn jest blisko. Zrobiłbym to zamiast tego:
Następnie zaimplementuj coś takiego:
Następnie uruchamiasz testy przeciwko TestClassToTest.
Powinna istnieć możliwość automatycznego generowania takich klas rozszerzeń przez analizowanie kodu. Nie zdziwiłbym się, gdyby PHPUnit już oferował taki mechanizm (chociaż nie sprawdziłem).
źródło
Wrzucę tutaj kapelusz na ring:
Użyłem hacka __call z różnym powodzeniem. Alternatywą, którą wymyśliłem, było użycie wzoru Visitor:
1: wygeneruj stdClass lub klasę niestandardową (aby wymusić typ)
2: zagruntuj to wymaganą metodą i argumentami
3: upewnij się, że twój SUT ma metodę acceptVisitor, która wykona metodę z argumentami określonymi w klasie odwiedzającej
4: wstrzyknij do klasy, którą chcesz przetestować
5: SUT wstrzykuje odwiedzającemu wynik operacji
6: zastosuj warunki testu do atrybutu wyniku Odwiedzającego
źródło
Rzeczywiście możesz użyć __call () w sposób ogólny, aby uzyskać dostęp do chronionych metod. Aby móc przetestować tę klasę
tworzysz podklasę w ExampleTest.php:
Zauważ, że metoda __call () nie odwołuje się w żaden sposób do klasy, więc możesz skopiować powyższe dla każdej klasy za pomocą chronionych metod, które chcesz przetestować, i po prostu zmienić deklarację klasy. Możesz być w stanie umieścić tę funkcję we wspólnej klasie bazowej, ale ja jej nie wypróbowałem.
Teraz sam przypadek testowy różni się tylko tym, w którym konstruuje się obiekt do testowania, zamieniając w PrzykładExposed na Przykład.
Wierzę, że PHP 5.3 pozwala używać refleksji do bezpośredniej zmiany dostępności metod, ale zakładam, że będziesz musiał to zrobić dla każdej metody osobno.
źródło
call_user_method_array()
Funkcja jest przestarzała od PHP 4.1.0 ... używaćcall_user_func_array(array($this, $method), $args)
zamiast. Zauważ, że jeśli używasz PHP 5.3.2+, możesz użyć Reflection, aby uzyskać dostęp do chronionych / prywatnych metod i atrybutówAccessible
pakiet, który używa refleksji, aby umożliwić testom dostęp do prywatnych / chronionych właściwości oraz metod klas i obiektów.__call()
wywoływany jest tylko wtedy, gdy dzwoniący nie ma dostępu do metody. Ponieważ klasa i jej podklasy mają dostęp do metod chronionych, wywołania do nich nie będą realizowane__call()
. Czy możesz opublikować kod, który nie działa w 5.2.7 w nowym pytaniu? Użyłem powyższego w 5.2 i przeszedłem do używania refleksji z 5.3.2.Proponuję następujące obejście obejścia / pomysłu Henrika Paula :)
Znasz nazwy prywatnych metod swojej klasy. Na przykład są one jak _add (), _edit (), _delete () itp.
Dlatego jeśli chcesz go przetestować pod kątem testowania jednostkowego, po prostu wywołaj metody prywatne przez prefiks i / lub sufiks jakiegoś wspólnego słowa (na przykład _addPhpunit), aby po wywołaniu metody __call () (ponieważ metoda _addPhpunit () nie istnieje) klasy właściciela, wystarczy wstawić potrzebny kod w metodzie __call (), aby usunąć prefiks / sufiks słowo / s (Phpunit), a następnie wywołać stamtąd wywnioskowaną metodę prywatną. To kolejne dobre użycie magicznych metod.
Wypróbuj to.
źródło