Jaki jest najlepszy sposób na testowanie jednostkowe metod chronionych i prywatnych w Rubim przy użyciu standardowego frameworka Rubiego Test::Unit
?
Jestem pewien, że ktoś się zepsuje i dogmatycznie zapewni, że „powinieneś testować jednostkowe tylko metody publiczne; jeśli wymaga testów jednostkowych, nie powinno to być metodą chronioną ani prywatną”, ale nie jestem zbyt zainteresowany debatowaniem nad tym. Mam kilka metod, które są chronione lub prywatne z dobrych i ważnych powodów, te metody prywatne / chronione są umiarkowanie złożone, a metody publiczne w klasie zależą od tych chronionych / prywatnych metod, które działają poprawnie, dlatego potrzebuję sposobu na przetestowanie metody chronione / prywatne.
Jeszcze jedno ... Generalnie wszystkie metody dla danej klasy umieszczam w jednym pliku, a testy jednostkowe dla tej klasy w innym pliku. Idealnie chciałbym, aby cała magia zaimplementowała tę funkcjonalność „testów jednostkowych metod chronionych i prywatnych” w pliku testu jednostkowego, a nie w głównym pliku źródłowym, aby główny plik źródłowy był tak prosty i bezpośredni, jak to tylko możliwe.
źródło
Odpowiedzi:
Możesz ominąć hermetyzację metodą wysyłania:
To jest „funkcja” Rubiego. :)
Podczas tworzenia Rubiego 1.9 toczyła się wewnętrzna debata, w której rozważano
send
poszanowanie prywatności isend!
ignorowanie jej, ale ostatecznie nic się nie zmieniło w Rubim 1.9. Zignoruj poniższe komentarze omawiającesend!
i niszczące rzeczy.źródło
send!
, to zostało cofnięte dawno temu,send/__send__
można wywołać metody wszelkiej widoczności - redmine.ruby-lang.org/repositories/revision/1?rev=13824public_send
(dokumentacja tutaj ), jeśli chcesz, aby uszanować prywatność. Myślę, że to nowość w Rubim 1.9.Oto jeden łatwy sposób, jeśli używasz RSpec:
źródło
Po prostu otwórz ponownie klasę w pliku testowym i przedefiniuj metodę lub metody jako publiczne. Nie musisz ponownie definiować wnętrzności samej metody, po prostu przekaż symbol do
public
wywołania.Jeśli oryginalna klasa jest zdefiniowana w ten sposób:
W swoim pliku testowym zrób coś takiego:
Możesz przekazać wiele symboli,
public
jeśli chcesz ujawnić bardziej prywatne metody.źródło
instance_eval()
może pomóc:Możesz go użyć, aby uzyskać bezpośredni dostęp do metod prywatnych i zmiennych instancji.
Możesz również rozważyć użycie
send()
, które da ci również dostęp do prywatnych i chronionych metod (jak zasugerował James Baker)Alternatywnie możesz zmodyfikować metaklasę obiektu testowego, aby uczynić metody prywatne / chronione publicznymi tylko dla tego obiektu.
Umożliwi to wywołanie tych metod bez wpływu na inne obiekty tej klasy. Możesz ponownie otworzyć klasę w katalogu testowym i upublicznić ją dla wszystkich instancji w kodzie testowym, ale może to mieć wpływ na test interfejsu publicznego.
źródło
Jednym ze sposobów, w jaki zrobiłem to w przeszłości, jest:
źródło
Możesz również refaktoryzować je do nowego obiektu, w którym te metody są publiczne, i delegować je prywatnie w oryginalnej klasie. Pozwoli ci to przetestować metody bez magicznej metarubii w twoich specyfikacjach, zachowując ich prywatność.
Jakie są te ważne powody? Inne języki OOP mogą w ogóle obejść się bez prywatnych metod (przychodzi na myśl smalltalk - gdzie prywatne metody istnieją tylko jako konwencja).
źródło
Podobnie jak w przypadku odpowiedzi @ WillSargent, oto czego użyłem w
describe
bloku w specjalnym przypadku testowania niektórych chronionych walidatorów bez konieczności przechodzenia przez ciężki proces tworzenia / aktualizowania ich za pomocą FactoryGirl (i możesz użyćprivate_instance_methods
podobnie):źródło
Aby upublicznić wszystkie chronione i prywatne metody dla opisanej klasy, możesz dodać następujący kod do swojego spec_helper.rb i nie musisz dotykać żadnego ze swoich plików specyfikacji.
źródło
Możesz "ponownie otworzyć" klasę i podać nową metodę, która deleguje do klasy prywatnej:
źródło
Prawdopodobnie skłaniałbym się ku użyciu instance_eval (). Zanim jednak dowiedziałem się o instance_eval (), utworzyłem klasę pochodną w moim pliku testu jednostkowego. Następnie ustawiłbym prywatne metody jako publiczne.
W poniższym przykładzie metoda build_year_range jest prywatna w klasie PublicationSearch :: ISIQuery. Wyprowadzenie nowej klasy tylko do celów testowych pozwala mi ustawić metodę (y) tak, aby były publiczne, a zatem bezpośrednio testowalne. Podobnie klasa pochodna ujawnia zmienną instancji o nazwie „result”, która wcześniej nie była ujawniona.
W moim teście jednostkowym mam przypadek testowy, który tworzy instancję klasy MockISIQuery i bezpośrednio testuje metodę build_year_range ().
źródło
We frameworku Test :: Unit można pisać,
Tutaj "nazwa_metody" jest metodą prywatną.
a podczas wywoływania tej metody można pisać,
źródło
Oto ogólny dodatek do Class, którego używam. To trochę bardziej strzelba niż tylko upublicznienie metody, którą testujesz, ale w większości przypadków nie ma to znaczenia i jest znacznie bardziej czytelna.
Używanie metody wysyłania w celu uzyskania dostępu do metod chronionych / prywatnych jest zepsute w wersji 1.9, więc nie jest zalecanym rozwiązaniem.
źródło
Aby poprawić pierwszą odpowiedź powyżej: w Rubim 1.9.1 to Object # send wysyła wszystkie wiadomości, a Object # public_send, który szanuje prywatność.
źródło
Zamiast obj.send możesz użyć metody singleton. To jeszcze 3 wiersze kodu w twojej klasie testowej i nie wymaga żadnych zmian w rzeczywistym kodzie do przetestowania.
W przypadkach testowych używasz wtedy,
my_private_method_publicly
gdy chcesz przetestowaćmy_private_method
.http://mathandprogramming.blogspot.com/2010/01/ruby-testing-private-methods.html
obj.send
dla metod prywatnych został zastąpionysend!
w 1.9, ale późniejsend!
został ponownie usunięty. Więcobj.send
działa doskonale.źródło
Wiem, że spóźniłem się na imprezę, ale nie testuj prywatnych metod… Nie mogę wymyślić powodu, żeby to zrobić. Metoda publicznie dostępna polega na użyciu gdzieś tej metody prywatnej, przetestowaniu metody publicznej i różnych scenariuszy, które spowodowałyby użycie tej metody prywatnej. Coś wchodzi, coś wychodzi. Testowanie metod prywatnych to wielkie nie-nie, a później znacznie trudniej jest zrefaktoryzować kod. Nie bez powodu są prywatne.
źródło
W tym celu:
Możesz to zaimplementować w swoim pliku test_helper:
źródło
Disrespect
naPrivacyViolator
(: P) i dokonałemdisrespect_privacy
tymczasowej edycji wiązania bloku, aby przypominać obiekt docelowy obiektowi otoki, ale tylko na czas bloku. W ten sposób nie musisz używać parametru bloku, możesz po prostu kontynuować odwoływanie się do obiektu o tej samej nazwie.