Jaka jest różnica między let
i a before
block w RSpec?
A kiedy używać każdego?
Jakie będzie dobre podejście (niech lub wcześniej) w poniższym przykładzie?
let(:user) { User.make !}
let(:account) {user.account.make!}
before(:each) do
@user = User.make!
@account = @user.account.make!
end
Przestudiowałem ten post o przepełnieniu stosu
Ale czy dobrze jest zdefiniować let dla rzeczy takich jak powyżej?
ruby-on-rails
unit-testing
rspec
kriysna
źródło
źródło
Odpowiedzi:
Wydaje się, że ludzie wyjaśnili niektóre podstawowe sposoby, w jakie się różnią, ale pominęli
before(:all)
i nie wyjaśniają dokładnie, dlaczego należy ich używać.Uważam, że zmienne instancji nie mają miejsca w większości specyfikacji, częściowo z powodów wymienionych w tej odpowiedzi , więc nie wspomnę o nich tutaj jako o opcji.
niech bloki
Kod w
let
bloku jest wykonywany tylko wtedy, gdy jest przywoływany, leniwe ładowanie oznacza to, że kolejność tych bloków jest nieistotna. Daje to dużą moc do ograniczenia powtarzających się konfiguracji w Twoich specyfikacjach.Jednym (bardzo małym i wymyślnym) przykładem tego jest:
let(:person) { build(:person) } subject(:result) { Library.calculate_awesome(person, has_moustache) } context 'with a moustache' do let(:has_moustache) { true } its(:awesome?) { should be_true } end context 'without a moustache' do let(:has_moustache) { false } its(:awesome?) { should be_false } end
Jak widać,
has_moustache
w każdym przypadku definiuje się to inaczej, ale nie ma potrzeby powtarzaniasubject
definicji. Należy zauważyć, żelet
zostanie użyty ostatni blok zdefiniowany w bieżącym kontekście. Jest to dobre przy ustawianiu wartości domyślnych, które mają być używane dla większości specyfikacji, które w razie potrzeby można nadpisać.Na przykład sprawdzenie zwracanej wartości,
calculate_awesome
jeśli przekazanoperson
model ztop_hat
ustawionym na true, ale bez wąsów, byłoby:context 'without a moustache but with a top hat' do let(:has_moustache) { false } let(:person) { build(:person, top_hat: true) } its(:awesome?) { should be_true } end
Kolejna rzecz, na którą należy zwrócić uwagę w przypadku bloków let, nie należy ich używać, jeśli szukasz czegoś, co zostało zapisane w bazie danych (tj.
Library.find_awesome_people(search_criteria)
), Ponieważ nie zostaną one zapisane w bazie danych, chyba że zostały już przywołane.let!
lubbefore
bloki są tym, co powinno być tutaj używane.Ponadto, nigdy nie używaj
before
do wyzwalania wykonywanialet
bloków, do tegolet!
jest stworzone!pozwolić! Bloki
let!
bloki są wykonywane w kolejności, w jakiej zostały zdefiniowane (podobnie jak przed blokiem). Jedyną podstawową różnicą w porównaniu z blokami przed blokami jest to, że otrzymujesz jawne odniesienie do tej zmiennej, zamiast konieczności powrotu do zmiennych instancji.Podobnie jak w przypadku
let
bloków, jeślilet!
zdefiniowano wiele bloków o tej samej nazwie, podczas wykonywania zostanie użyty najnowszy. Podstawowa różnica polega na tym, żelet!
bloki będą wykonywane wiele razy, jeśli zostaną użyte w ten sposób, podczas gdylet
blok będzie wykonywany tylko ostatnim razem.przed (: każdym) blokami
before(:each)
jest ustawieniem domyślnym przed blokiem i dlatego można do niego odwoływać się jakobefore {}
zamiast określania pełnego zabefore(:each) {}
każdym razem.Osobiście wolę używać
before
bloków w kilku podstawowych sytuacjach. Użyję przed blokami, jeśli:before { get :index }
.). Nawet jeślisubject
w wielu przypadkach możesz tego użyć , czasami wydaje się to bardziej wyraźne, jeśli nie potrzebujesz odniesienia.Jeśli zauważysz, że piszesz duże
before
bloki dla swoich specyfikacji, sprawdź swoje fabryki i upewnij się, że w pełni rozumiesz cechy i ich elastyczność.przed (: wszystkie) bloki
Są one wykonywane tylko raz, przed specyfikacjami w bieżącym kontekście (i jego potomkach). Można je wykorzystać z wielką korzyścią, jeśli zostaną poprawnie napisane, ponieważ w pewnych sytuacjach może to ograniczyć wykonanie i wysiłek.
Jednym z przykładów (który prawie w ogóle nie wpłynąłby na czas wykonania) jest wyśmiewanie zmiennej ENV na potrzeby testu, co należy zrobić tylko raz.
Mam nadzieję, że to pomoże :)
źródło
before(:all)
opcja nie istnieje w programie Minitest. Oto kilka obejść w komentarzach: github.com/seattlerb/minitest/issues/61its
nie jest już w rspec-core. Jest to bardziej nowoczesny, idiomatyczny sposóbit { is_expected.to be_awesome }
.Prawie zawsze wolę
let
. Post, który łączysz, wskazuje, żelet
jest również szybszy. Jednak czasami, gdy trzeba wykonać wiele poleceń, mogę użyć,before(:each)
ponieważ jego składnia jest bardziej przejrzysta, gdy zaangażowanych jest wiele poleceń.W twoim przykładzie zdecydowanie wolałbym użyć
let
zamiastbefore(:each)
. Ogólnie rzecz biorąc, gdy wykonywana jest inicjalizacja tylko niektórych zmiennych, lubię używaćlet
.źródło
Duża różnica, o której nie wspomniano, polega na tym, że zmienne zdefiniowane za pomocą
let
nie są tworzone, dopóki nie wywołasz ich po raz pierwszy. Tak więc, podczas gdybefore(:each)
blok utworzyłby wystąpienie wszystkich zmiennych,let
pozwólmy Ci zdefiniować liczbę zmiennych, których możesz użyć w wielu testach, nie tworzy ich automatycznie. Nie wiedząc o tym, Twoje testy mogą powrócić i ugryźć się, jeśli spodziewasz się, że wszystkie dane zostaną wcześniej załadowane. W niektórych przypadkach możesz nawet chcieć zdefiniować kilkalet
zmiennych, a następnie użyćbefore(:each)
bloku do wywołania każdejlet
instancji, aby upewnić się, że dane są dostępne na początek.źródło
let!
do zdefiniowania metod, które są wywoływane przed każdym przykładem. Zobacz dokumentację RSpec .Wygląda na to, że używasz Machinist. Uwaga, możesz zauważyć pewne problemy z marką! wewnątrz let (wersja non-bang) dzieje się poza globalną transakcją urządzeń (jeśli używasz również urządzeń transakcyjnych), co powoduje uszkodzenie danych do innych testów.
źródło