Zwykle używam przed blokami do ustawiania zmiennych instancji. Następnie używam tych zmiennych w moich przykładach. Niedawno się natknąłem let()
. Według dokumentów RSpec jest do tego przyzwyczajony
... aby zdefiniować zapamiętaną metodę pomocnika. Wartość będzie buforowana dla wielu połączeń w tym samym przykładzie, ale nie w przykładach.
Czym różni się to od używania zmiennych instancji w blokach przed? A także kiedy powinieneś używać let()
vs before()
?
Odpowiedzi:
Zawsze wolę
let
zmienną instancji z kilku powodów:nil
, co może prowadzić do subtelnych błędów i fałszywych alarmów. Ponieważlet
tworzy metodę, otrzymaszNameError
błąd, gdy ją przeliterujesz, co uważam za lepsze. Ułatwia to również refaktoryzację specyfikacji.before(:each)
Hak zostanie uruchomiony przed każdym przykładzie, nawet jeśli przykład nie korzysta z żadnego z tych zmiennych instancji określonymi w haku. Zwykle nie jest to wielka sprawa, ale jeśli konfiguracja zmiennej instancji zajmuje dużo czasu, to marnujesz cykle. W przypadku metody zdefiniowanej przezlet
kod inicjujący działa tylko wtedy, gdy wywołuje go przykład.@
).let
i utrzymywanie mojegoit
bloku ładnym i krótkim.Powiązany link można znaleźć tutaj: http://www.betterspecs.org/#let
źródło
let
zdefiniowanie wszystkich zależnych obiektów i użyciebefore(:each)
do skonfigurowania potrzebnej konfiguracji lub wszelkich próbnych / skrótów potrzebnych w przykładach. Wolę to od jednego dużego przed hakiem zawierającym to wszystko. Ponadtolet(:foo) { Foo.new }
jest mniej hałaśliwy (i bardziej do rzeczy)before(:each) { @foo = Foo.new }
. Oto przykład tego, jak go używam: github.com/myronmarston/vcr/blob/v1.7.0/spec/vcr/util/…NoMethodError
ostrzeżenie, ale YMMV.foo = Foo.new(...)
a następnie użytkownikówfoo
w późniejszych wierszach. Później piszesz nowy przykład w tej samej grupie przykładów, która również potrzebujeFoo
instancji w ten sam sposób. W tym momencie chcesz refaktoryzować, aby wyeliminować duplikację. Możesz usunąćfoo = Foo.new(...)
wiersze z przykładów i zastąpić je bezlet(:foo) { Foo.new(...) }
zmiany sposobu użycia przykładówfoo
. Ale jeśli zmienisz fakturę,before { @foo = Foo.new(...) }
musisz także zaktualizować odniesienia w przykładach odfoo
do@foo
.Różnica między używaniem zmiennych instancji a
let()
tym, żelet()
jest leniwa . Oznacza to, żelet()
nie jest oceniane, dopóki metoda, którą określa, nie zostanie uruchomiona po raz pierwszy.Różnica między
before
ilet
to, żelet()
daje dobry sposób na zdefiniowanie grupy zmiennych w stylu „kaskadowym”. W ten sposób specyfikacja wygląda nieco lepiej, upraszczając kod.źródło
let
jeśli za każdym razem potrzebujesz czegoś do oceny? np. potrzebuję modelu potomnego, który będzie obecny w bazie danych, zanim zostanie wywołane pewne zachowanie w modelu nadrzędnym. Niekoniecznie odnoszę się do tego modelu potomnego w teście, ponieważ testuję zachowanie modeli nadrzędnych. W tej chwili korzystam zlet!
metody, ale może byłoby bardziej wyraźne, aby wprowadzić tę konfiguracjębefore(:each)
?Całkowicie zastąpiłem wszystkie zastosowania zmiennych instancji w moich testach rspec, aby użyć let (). Napisałem szybki numerek dla znajomego, który wykorzystał go do nauczania małej klasy Rspec: http://ruby-lambda.blogspot.com/2011/02/agile-rspec-with-let.html
Jak mówi niektóre inne odpowiedzi tutaj, let () jest leniwy, więc załaduje tylko te, które wymagają załadowania. DRY poprawia specyfikację i czyni ją bardziej czytelną. W rzeczywistości przeniosłem kod Rspec let () do użycia w moich kontrolerach, w stylu gem inherited_resource. http://ruby-lambda.blogspot.com/2010/06/stealing-let-from-rspec.html
Oprócz leniwej oceny, drugą zaletą jest to, że w połączeniu z ActiveSupport :: Concern oraz wczytywaniem / specyfikacją / wsparciem / zachowaniem możesz stworzyć własną specyfikację mini-DSL specyficzną dla twojej aplikacji. Napisałem te do testowania w stosunku do zasobów Rack i RESTful.
Używam strategii Fabrycznie wszystko (przez Machinist + Forgery / Faker). Możliwe jest jednak użycie go w połączeniu z blokami przed (: każdy), aby wstępnie załadować fabryki dla całego zestawu przykładowych grup, umożliwiając szybsze działanie specyfikacji: http://makandra.com/notes/770-taking-ainstage -of-rspec-s-let-in-before-blocks
źródło
# spec/friendship_spec.rb
i# spec/comment_spec.rb
przykład, czy nie uważasz, że sprawiają, że jest mniej czytelny? Nie mam pojęcia, skądusers
pochodzą i będę musiał kopać głębiej.let()
z ostatnich kilku dni i osobiście nie widzę różnicy, z wyjątkiem pierwszej korzyści, o której wspomniał Myron. Nie jestem pewien, czy mogę odpuścić, a co nie, może dlatego, że jestem leniwy i lubię widzieć kod z góry bez konieczności otwierania kolejnego pliku. Dziękuję za komentarze.Ważne jest, aby pamiętać, że let jest oceniany leniwie i nie wprowadza w nim metod skutków ubocznych, w przeciwnym razie nie byłbyś w stanie łatwo zmienić z let na wcześniej (: każdy) . Możesz użyć let! zamiast pozwolić , aby był oceniany przed każdym scenariuszem.
źródło
Ogólnie rzecz biorąc,
let()
jest to ładniejsza składnia i oszczędza pisania@name
symboli w dowolnym miejscu. Ale, zastrzegaczu! Odkryłem, żelet()
wprowadza również subtelne błędy (lub przynajmniej drapanie głowy), ponieważ zmienna tak naprawdę nie istnieje, dopóki nie spróbujesz jej użyć ... Powiedz znak opowieści: jeśli dodasz znakputs
po,let()
aby zobaczyć, że zmienna jest poprawna, pozwala na specyfikację przekazać, ale bezputs
specyfikacji zawodzi - znalazłeś tę subtelność.Odkryłem również, że
let()
nie wydaje się buforować w każdych okolicznościach! Napisałem to na moim blogu: http://technicaldebt.com/?p=1242Może to tylko ja?
źródło
let
zawsze zapamiętuje wartość na czas trwania jednego przykładu. Nie zapamiętuje wartości w wielu przykładach.before(:all)
, natomiast pozwala ponownie użyć zainicjowanej zmiennej w wielu przykładach.let!
służy. relishapp.com/rspec/rspec-core/v/2-6/docs/helper-methods/…let jest funkcjonalny, ponieważ zasadniczo jest proc. Również jest buforowany.
Jeden problem, który znalazłem od razu z let ... W bloku Spec, który ocenia zmianę.
Musisz koniecznie zadzwonić
let
poza oczekiwany blok. tzn. dzwoniszFactoryGirl.create
w let Let Block. Zazwyczaj robię to, sprawdzając, czy obiekt jest utrwalony.W przeciwnym razie, gdy
let
blok zostanie wywołany po raz pierwszy, zmiana w bazie danych rzeczywiście nastąpi z powodu opóźnionego tworzenia instancji.Aktualizacja
Właśnie dodam notatkę. Ostrożnie graj golfa kodowego lub w tym przypadku rspec golfa z tą odpowiedzią.
W takim przypadku muszę po prostu wywołać metodę, na którą obiekt reaguje. Więc wzywam
_.persisted?
wywołuję metodę _ na obiekcie jako jego prawdziwość. Próbuję tylko utworzyć obiekt. Mógłbyś zadzwonić pusty? czy zero? też. Nie chodzi o test, ale o powołanie obiektu do życia, nazywając go.Więc nie możesz refaktoryzować
być
ponieważ obiekt nie został utworzony ... jest leniwy. :)
Aktualizacja 2
wykorzystać let! składnia do natychmiastowego tworzenia obiektów, co powinno całkowicie uniknąć tego problemu. Zauważ jednak, że pokona on wiele celów lenistwa bez uderzeń.
Również w niektórych przypadkach możesz chcieć wykorzystać składnię tematu zamiast pozwolić, ponieważ może to dać dodatkowe opcje.
źródło
Uwaga dla Josepha - jeśli tworzysz obiekty bazy danych w,
before(:all)
nie zostaną one przechwycone w transakcji i istnieje większe prawdopodobieństwo pozostawienia cruft w testowej bazie danych. Użyjbefore(:each)
zamiast tego.Innym powodem użycia let i jego leniwej oceny jest to, że możesz wziąć skomplikowany obiekt i przetestować poszczególne elementy, zastępując let w kontekstach, jak w tym bardzo wymyślonym przykładzie:
źródło
Domyślnie „przed” oznacza
before(:each)
. Odnośnik The Rspec Book, copyright 2010, strona 228.Używam
before(:each)
do inicjowania niektórych danych dla każdej przykładowej grupy bez konieczności wywoływanialet
metody tworzenia danych w bloku „it”. W tym przypadku mniej kodu w bloku „it”.Używam,
let
jeśli chcę niektóre dane w niektórych przykładach, ale nie w innych.Zarówno przed, jak i niech są świetne do OSUSZANIA bloków „it”.
Aby uniknąć nieporozumień, „let” to nie to samo, co
before(:all)
. „Let” ponownie ocenia swoją metodę i wartość dla każdego przykładu („it”), ale buforuje wartość w wielu wywołaniach w tym samym przykładzie. Możesz przeczytać więcej na ten temat tutaj: https://www.relishapp.com/rspec/rspec-core/v/2-6/docs/helper-methods/let-and-letźródło
Odmienny głos tutaj: po 5 latach rspec bardzo mi się nie podoba
let
.1. Leniwa ocena często powoduje, że konfiguracja testu jest myląca
Trudno jest uzasadnić konfigurację, gdy niektóre rzeczy zadeklarowane w konfiguracji nie wpływają na stan, podczas gdy inne tak.
W końcu z frustracji ktoś po prostu zmienia się
let
nalet!
(to samo bez leniwej oceny), aby jego specyfikacja działała. Jeśli to zadziała, rodzi się nowy nawyk: kiedy nowa specyfikacja jest dodawana do starszego pakietu i nie działa, pierwszą rzeczą, którą pisze autor, jest dodanie grzywki do losowychlet
połączeń.Wkrótce zniknęły wszystkie korzyści związane z wydajnością.
2. Specjalna składnia jest nietypowa dla użytkowników spoza rspec
Wolę uczyć Ruby w moim zespole niż sztuczek rspec. Zmienne instancji lub wywołania metod są przydatne wszędzie w tym projekcie i innych,
let
składnia przyda się tylko w rspec.3. „Korzyści” pozwalają nam łatwo zignorować dobre zmiany w projekcie
let()
jest dobry w przypadku kosztownych zależności, których nie chcemy tworzyć w kółko. Pasuje również dosubject
, co pozwala wysuszyć powtarzające się wywołania metod wieloparametrowychDrogie zależności powtarzane wiele razy, a metody z dużymi podpisami są punktami, w których moglibyśmy ulepszyć kod:
We wszystkich tych przypadkach mogę rozwiązać problem trudnych testów za pomocą kojącego balsamu magii rspec lub spróbować rozwiązać problem. Wydaje mi się, że spędziłem zbyt wiele z ostatnich kilku lat na tym pierwszym i teraz chcę trochę lepszego kodu.
Aby odpowiedzieć na pierwotne pytanie: wolałbym nie, ale nadal używam
let
. Używam go głównie do dopasowania się do stylu reszty zespołu (wygląda na to, że większość programistów Railsów na świecie jest teraz głęboko zanurzona w swojej magii rspec, więc to bardzo często). Czasami używam go, gdy dodam test do kodu, nad którym nie mam kontroli, lub nie mam czasu na refaktoryzację do lepszej abstrakcji: tj. Gdy jedyną opcją jest środek przeciwbólowy.źródło
Używam
let
do testowania odpowiedzi HTTP 404 w specyfikacji API za pomocą kontekstów.Aby utworzyć zasób, używam
let!
. Ale do przechowywania identyfikatora zasobu używamlet
. Zobacz, jak to wygląda:Dzięki temu specyfikacje są czyste i czytelne.
źródło