Wygląda na to, że istnieją dwa zupełnie różne podejścia do testowania i chciałbym zacytować oba.Testowanie: jak skupić się na zachowaniu zamiast na implementacji bez utraty prędkości?
Chodzi o to, że te opinie zostały wypowiedziane 5 lat temu (2007) i jestem zainteresowany, co zmieniło się od tego czasu i w którą stronę powinienem jechać.
W teorii jest to, że testy mają być agnostykiem realizacji . Prowadzi to do mniej kruchych testów, a właściwie wyników (lub zachowania).
Dzięki RSpec, czuję, że wspólne podejście do kpienia z modeli w celu przetestowania kontrolerów kończy się zmusza do zbytniego spojrzenia na zbyt dużą liczbę do implementacji kontrolera.
To samo w sobie nie jest takie złe, ale problem polega na tym, że zbyt wiele uwagi poświęca kontrolerowi, aby określić, w jaki sposób model jest używany. Dlaczego ma znaczenie, jeśli mój kontroler wywoła Thing.new? Co się stanie, jeśli mój kontroler zdecyduje, że przejmie Thing.create! i trasa ratunkowa? Co się stanie, jeśli mój model ma specjalną metodę inicjowania , np. Thing.build_with_foo? Moja specyfikacja dla zachowania nie powinna zawieść, jeśli zmienię implementację.
Ten problem jest jeszcze gorszy, gdy masz zagnieżdżone zasoby i jest , tworząc wiele modeli na kontroler. Niektóre z moich metod konfiguracji kończą się na 15 lub więcej liniach i są BARDZO delikatne.
Intencją RSpec jest całkowite odizolowanie logiki sterownika od Twoich modeli, co brzmi dobrze w teorii, ale prawie działa w stosunku do ziarna dla zintegrowanego stosu, takiego jak szyny. Zwłaszcza jeśli ćwiczysz dyscyplinę chudego kontrolera/modelu tłuszczu, ilość logiki w sterowniku staje się bardzo mała, a konfiguracja staje się ogromna.
Co więc chce zrobić ktoś z BDD? Cofając się, zachowanie, które naprawdę chcę przetestować, nie polega na tym, że mój kontroler nazywa Thing.new, ale , który podał parametry X, tworzy nową rzecz i przekierowuje do niego.
David Chelimsky:
Chodzi o kompromisy.
Fakt, że AR wybiera dziedziczenie zamiast delegacja stawia nas w się wiążą testów - musimy być połączony z bazą danych lub musimy być bardziej intymny z realizacją. Akceptujemy ten wybór projektu , ponieważ czerpiemy korzyści z ekspresji i SUCHOŚCI.
Łamiąc się z dylematem, wybrałem szybsze testy kosztem nieco bardziej kruche. Wybierasz mniej kruche testy kosztem z nich działa nieco wolniej. To kompromis w obie strony.
W praktyce biegnę testów setki, jeśli nie tysiące razy dni (używam autotest i podjąć kroki bardzo ziarnistych) i zmienić czy używam „nowy” lub „tworzyć” prawie nigdy. Również ze względu na szczegółowe kroki, nowe modele , które pojawiają się na początku są dość niestabilne. Podejście valid_thing_attrs minimalizuje ból z tego powodu, ale wciąż oznacza, że każde nowe wymagane pole oznacza, że muszę zmienić valid_thing_attrs.
Ale jeśli twoje podejście działa dla ciebie w praktyce, to jest dobre! W rzeczywistości, , zdecydowanie zaleciłbym opublikowanie wtyczki z generatorami , które produkują przykłady tak, jak lubisz. Jestem pewien, że wiele osób odniosłoby z tego korzyści.
Z ciekawości, jak często używacie mocks w badaniach/specyfikacji? Być może robię coś złego, ale znajduję to poważnie ograniczające. Od przejścia na rSpec ponad miesiąc temu, robiłem , co zalecają w dokumentach, w których kontroler i warstwy widoku nie trafiają w bazę danych, a modele są całkowicie wyśmiewane obecnie. Daje to miłe przyspieszenie i sprawia, że niektóre rzeczy łatwiej, , ale uważam, że wady to znacznie przewyższają zalety. Od za pomocą makiet, moje specyfikacje zamieniły się w koszmar utrzymania. Specyfikacje służą do testowania zachowania, a nie jego implementacji. Nie obchodzi mnie , jeśli została wywołana metoda Chcę po prostu upewnić się, że wynik wyjściowy jest poprawny. Ponieważ szyderstwo sprawia, że specyfikacja jest wybredna w stosunku do implementacji , to sprawia, że proste refaktoryzacje (które nie zmieniają zachowania ) są niemożliwe bez konieczności ciągłego wracania i "poprawiania" specyfikacji. Jestem bardzo przekonany o tym, co powinien zawierać opis/testy: . Test powinien się rozpaść tylko wtedy, gdy aplikacja się zepsuje. Jest to jeden z powodów, dla których nie testuję warstwy widoku, ponieważ uważam ją za zbyt sztywną. Często prowadzi to do przerwania testów bez przerywania aplikacji, gdy zmienia się małe rzeczy w widoku. Ten sam problem występuje z mockami . Do tego wszystkiego, właśnie zdałem sobie sprawę, że dzisiaj szyderstwa/karczowania metoda klasy (czasami) trzyma się pomiędzy specyfikacjami. Specyfikacje powinny być samodzielne i nie powinny być pod wpływem innych specyfikacji. To łamie tę regułę i prowadzi do trudnych błędów. Czego nauczyłem się z tego wszystkiego? Bądź ostrożny, gdy używasz szyderstwa. Stubbing nie jest tak zły, ale wciąż ma kilka takich samych problemów.
Wziąłem w ciągu ostatnich kilku godzin i usunąłem prawie wszystkie mocks z mojej specyfikacji. Połączyłem również kontroler i wyświetl specyfikacje w jeden przy użyciu "integracją_wyników" w specyfikacji kontrolera. Ładuję również wszystkie urządzenia dla każdej specyfikacji kontrolera, więc istnieje kilka danych testowych do wypełnienia widoków. Wynik końcowy? Moje specyfikacje są krótsze, prostsze, bardziej spójne, mniej sztywne i testują cały stos razem (model, widok, kontroler), aby żadne błędy nie mogły się zepsuć. Jestem nie mówiąc, że jest to "właściwa" droga dla wszystkich. Jeśli Twój projekt wymaga bardzo surowego przypadku spec, to może nie być dla ciebie, ale w moim przypadku jest to lepsze na świecie niż to, co miałem przed użyciem makiet.Wciąż myślę, że stubbing jest dobrym rozwiązaniem w kilku miejscach, więc nadal robię .
To jest dobre pytanie; Widziałem wiele kruchych testów jednostkowych w wyniku całego szyderstwa, które się dzieje.Testy jednostek JavaScript mogą być gorsze. –
Człowieku, podoba mi się te wszystkie myśli! Największym problemem jest prawdopodobnie Rails. W GOOS mówią, że nie powinieneś kpić z kodu strony trzeciej (która zawierałaby ActiveRecord), ponieważ nie możesz wprowadzać zmian w projekcie, które mają na to wpływ. Zasadniczo jest to problem, David zauważa, że wolałby raczej używać kompozycji, ale nie może b/c AR wybrać dziedziczenia (jeśli chce robić rzeczy tak, jak robiono to tradycyjnie w Railsach). Osobiście mam wiele pomysłów i nie wiem, które z nich są właściwe. Dobre pytanie. –