Testuję model z wywołaniem zwrotnym po utworzeniu, które chciałbym uruchamiać tylko czasami podczas testowania. Jak mogę pominąć / uruchomić wywołania zwrotne z fabryki?
class User < ActiveRecord::Base
after_create :run_something
...
end
Fabryka:
FactoryGirl.define do
factory :user do
first_name "Luiz"
last_name "Branco"
...
# skip callback
factory :with_run_something do
# run callback
end
end
ruby-on-rails
rspec
factory-bot
luizbranco
źródło
źródło
:on => :create
walidację, użyjafter(:build) { |user| user.class.skip_callback(:validate, :create, :after, :run_something) }
Class.skip_callback
wywołanie będzie trwałe w innych testach, więc jeśli inne testy oczekują wystąpienia wywołania zwrotnego, zakończą się niepowodzeniem, jeśli spróbujesz odwrócić logikę pominięcia wywołania zwrotnego.after(:build)
bloku. Umożliwia to fabryczne uruchomienie oddzwaniania i nie wymaga resetowania oddzwaniania po każdym użyciu.Jeśli nie chcesz uruchamiać wywołania zwrotnego, wykonaj następujące czynności:
Pamiętaj, że funkcja skip_callback będzie trwała w innych specyfikacjach po uruchomieniu, dlatego rozważ coś takiego:
źródło
Żadne z tych rozwiązań nie jest dobre. Niszczą klasę, usuwając funkcje, które powinny zostać usunięte z instancji, a nie z klasy.
Zamiast blokować wywołanie zwrotne, pomijam jego funkcjonalność. W pewnym sensie bardziej podoba mi się to podejście, ponieważ jest bardziej wyraźne.
źródło
around_*
(npuser.define_singleton_method(:around_callback_method){|&b| b.call }
.).Chciałbym ulepszyć odpowiedź @luizbranco, aby wywołanie zwrotne after_save było bardziej użyteczne podczas tworzenia innych użytkowników.
Działa bez funkcji zwrotnej after_save:
Uruchamianie z wywołaniem zwrotnym after_save:
W moim teście wolę domyślnie tworzyć użytkowników bez wywołania zwrotnego, ponieważ używane metody uruchamiają dodatkowe rzeczy, których normalnie nie chcę w moich przykładach testowych.
---------- AKTUALIZACJA ------------ Przestałem używać skip_callback, ponieważ wystąpiły problemy z niespójnością w zestawie testów.
Alternatywne rozwiązanie 1 (użycie stub i unstub):
Alternatywne rozwiązanie 2 (moje preferowane podejście):
źródło
Rails 5 -
skip_callback
podnoszenie błędu argumentu podczas przeskakiwania z fabryki FactoryBot.Nastąpiła zmiana w Railsach 5, w jaki sposób skip_callback obsługuje nierozpoznane wywołania zwrotne:
W przypadku
skip_callback
wywołania z fabryki rzeczywisty callback w modelu AR nie jest jeszcze zdefiniowany.Jeśli spróbowałeś wszystkiego i wyrwałeś sobie włosy jak ja, oto twoje rozwiązanie (wziąłem je z wyszukiwania problemów z FactoryBotem) ( UWAGA na
raise: false
część ):Możesz go używać z dowolnymi innymi strategiami, które preferujesz.
źródło
To rozwiązanie działa dla mnie i nie musisz dodawać dodatkowego bloku do definicji fabryki:
źródło
Ważna uwaga , należy określić oba z nich. Jeśli użyjesz tylko wcześniej i uruchomisz wiele specyfikacji, spróbuje wyłączyć wywołanie zwrotne wiele razy. Za pierwszym razem się powiedzie, ale za drugim callback nie będzie już definiowany. Więc się pomylisz
źródło
W Rspec 3 najlepiej sprawdził się prosty kod
źródło
User
;:run_something
nie jest metodą klasową.Dzwonienie do skip_callback z mojej fabryki okazało się dla mnie problematyczne.
W moim przypadku mam klasę dokumentu z niektórymi wywołaniami zwrotnymi związanymi z s3 przed i po utworzeniu, które chcę uruchomić tylko wtedy, gdy konieczne jest przetestowanie pełnego stosu. W przeciwnym razie chcę pominąć te wywołania zwrotne s3.
Kiedy próbowałem skip_callbacks w mojej fabryce, utrzymywało się, że wywołanie zwrotne pomijało nawet wtedy, gdy utworzyłem obiekt dokumentu bezpośrednio, bez użycia fabryki. Zamiast tego użyłem odcinków mokki w wywołaniu po kompilacji i wszystko działa idealnie:
źródło
before_validation
hakiem (starając się zrobićskip_callback
z dowolnego FactoryGirl użytkownikabefore
lubafter
wariantówbuild
icreate
nie działa)Będzie to działać z aktualną składnią rspec (od tego postu) i jest znacznie czystsze:
źródło
Odpowiedź Jamesa Chevaliera na temat tego, jak pominąć wywołanie zwrotne before_validation nie pomogła mi, więc jeśli masz to samo co ja, tutaj działa rozwiązanie:
w modelu:
w fabryce:
źródło
Model.skip_callback(...)
W moim przypadku mam wywołanie zwrotne ładujące coś do mojej pamięci podręcznej Redis. Ale potem nie miałem / nie chciałem, aby instancja redis działała w moim środowisku testowym.
W mojej sytuacji, podobnie jak powyżej, po prostu zablokowałem moją
load_to_cache
metodę w moim spec_helper, z:Ponadto w pewnej sytuacji, w której chcę to przetestować, muszę po prostu odblokować je w bloku before odpowiednich przypadków testowych Rspec.
Wiem, że możesz mieć coś bardziej skomplikowanego w twoim
after_create
życiu lub może nie być to zbyt eleganckie. Możesz spróbować anulować wywołanie zwrotne zdefiniowane w twoim modelu, definiującafter_create
hook w swoim Factory (patrz dokumentacja factory_girl), gdzie prawdopodobnie możesz zdefiniować to samo callback i returnfalse
, zgodnie z sekcją „Canceling callback” tego artykułu . (Nie jestem pewien, w jakiej kolejności wykonywane są wywołania zwrotne, dlatego nie wybrałem tej opcji).Na koniec (przepraszam, że nie mogę znaleźć tego artykułu) Ruby pozwala ci użyć jakiegoś brudnego metaprogramowania do odczepienia połączenia zwrotnego (będziesz musiał go zresetować). Myślę, że byłaby to najmniej preferowana opcja.
Cóż, jest jeszcze jedna rzecz, nie do końca rozwiązanie, ale zobacz, czy możesz uciec z Factory.build w swoich specyfikacjach, zamiast faktycznie tworzyć obiekt. (Byłoby to najprostsze, gdybyś mógł).
źródło
Odnośnie odpowiedzi zamieszczonej powyżej, https://stackoverflow.com/a/35562805/2001785 , nie musisz dodawać kodu do fabryki. Zauważyłem, że łatwiej jest przeładować metody w samych specyfikacjach. Na przykład zamiast (w połączeniu z kodem fabrycznym w cytowanym poście)
Lubię używać (bez cytowanego kodu fabrycznego)
W ten sposób nie musisz patrzeć zarówno na pliki fabryczne, jak i na pliki testowe, aby zrozumieć zachowanie testu.
źródło
Zauważyłem, że poniższe rozwiązanie jest bardziej przejrzyste, ponieważ wywołanie zwrotne jest uruchamiane / ustawiane na poziomie klasy.
źródło
Oto fragment utworzony przeze mnie, aby poradzić sobie z tym w ogólny sposób.
Pominie wszystkie skonfigurowane wywołania zwrotne, w tym wywołania zwrotne związane z railsami, takie jak
before_save_collection_association
, ale nie pominie niektórych potrzebnych do poprawnego działania ActiveRecord, na przykładautosave_associated_records_for_
wywołań zwrotnych generowanych automatycznie .potem później:
Nie trzeba dodawać, YMMV, więc spójrz w dziennikach testów, co tak naprawdę pomijasz. Może masz klejnot, dodając oddzwonienie, którego naprawdę potrzebujesz, i sprawi, że twoje testy będą nieudane lub z twojego modelu tłuszczu 100 wywołań zwrotnych potrzebujesz tylko pary do konkretnego testu. W takich przypadkach wypróbuj przejściowy
:force_callbacks
PREMIA
Czasami musisz również pominąć walidację (wszystko po to, aby testy były szybsze), a następnie spróbuj z:
źródło
Możesz po prostu ustawić wywołanie zwrotne z cechą dla tych instancji, kiedy chcesz je uruchomić.
źródło