- Aktualizujte svůj soubor Gemfile
- Konfigurujte sadu testů
- Test::Unit
- Cucumber
- Spinach
- Minitest
- Minitest::Spec
- minitest-rails
- Definice továren
- Použití faktorů
- Dynamické atributy
- Aliasy
- Závislé atributy
- Přechodné atributy
- Atributy názvů metod / rezervovaných slov
- Dědičnost
- Asociace
- Sekvence
- Traity
- Zpětná volání
- Modifikace továren
- Vytváření nebo vytváření více záznamů
- Lintování továren
- Vlastní konstrukce
- Vlastní strategie
- Vlastní zpětná volání
- Vlastní metody pro perzistenci objektů
- ActiveSupport Instrumentation
- Rails Preloadery a RSpec
- Používání bez Bundleru
Aktualizujte svůj soubor Gemfile
Pokud používáte Rails, budete muset změnit požadovanou verzi factory_girl_rails
:
gem "factory_girl_rails", "~> 4.0"
Pokud nepoužíváte Rails, stačí změnit požadovanou verzi factory_girl
:
gem "factory_girl", "~> 4.0"
export JRUBY_OPTS=--1.9
Po aktualizaci souboru Gemfile budete chtít aktualizovat svůj balíček.
Konfigurujte sadu testů
Nezapomeňte ve svém rails_helperu vyžadovat výše uvedený soubor, protože složka support se nenačítá eagerly
require 'support/factory_girl'
Test::Unit
class Test::Unit::TestCase include FactoryGirl::Syntax::Methodsend
Cucumber
# env.rb (Rails example location - RAILS_ROOT/features/support/env.rb)World(FactoryGirl::Syntax::Methods)
Spinach
class Spinach::FeatureSteps include FactoryGirl::Syntax::Methodsend
Minitest
class Minitest::Unit::TestCase include FactoryGirl::Syntax::Methodsend
Minitest::Spec
class Minitest::Spec include FactoryGirl::Syntax::Methodsend
minitest-rails
class ActiveSupport::TestCase include FactoryGirl::Syntax::Methodsend
Pokud do testovací sady nezahrnete FactoryGirl::Syntax::Methods
, pak všechny metody factory_girl budou muset být uvozeny FactoryGirl
.
Definice továren
Každá továrna má název a sadu atributů. Podle názvu se ve výchozím nastavení odhaduje třída objektu, ale je možné jej explicitně zadat:
Důrazně se doporučuje, abyste pro každou třídu měli jednu továrnu, která poskytuje nejjednodušší sadu atributů potřebných k vytvoření instance dané třídy. Pokud vytváříte objekty ActiveRecord, znamená to, že byste měli poskytovat pouze atributy, které jsou vyžadovány prostřednictvím validace a které nemají výchozí hodnoty. Další továrny mohou být vytvořeny prostřednictvím dědičnosti, aby pokryly běžné scénáře pro každou třídu.
Pokus o definování více továren se stejným názvem vyvolá chybu.
Faktory lze definovat kdekoli, ale budou automaticky načteny po vyvolání FactoryGirl.find_definitions
, pokud jsou faktory definovány v souborech na těchto místech:
test/factories.rbspec/factories.rbtest/factories/*.rbspec/factories/*.rb
Použití faktorů
factory_girl podporuje několik různých strategií sestavování: build, create, attributes_for a build_stubbed:
Nezáleží na tom, která strategie je použita, je možné nadefinované atributy přepsat předáním hashe:
# Build a User instance and override the first_name propertyuser = build(:user, first_name: "Joe")user.first_name# => "Joe"
Dynamické atributy
Většinu atributů továrny lze přidat pomocí statických hodnot, které jsou vyhodnoceny při definování továrny, ale některé atributy (například asociace a další atributy, které musí být dynamicky generovány) budou potřebovat hodnoty přiřazené při každém generování instance. Tyto „dynamické“ atributy lze přidat předáním bloku místo parametru:
factory :user do # ... activation_code { User.generate_activation_code } date_of_birth { 21.years.ago }end
Vzhledem k blokové syntaxi v jazyce Ruby vyžaduje definování atributů jako Hash
es (například proserializované sloupce/JSON) dvě sady kudrlinkových závorek:
factory :program do configuration { { auto_resolve: false, auto_define: true } }end
Aliasy
factory_girl umožňuje definovat aliasy k existujícím továrnám, aby bylo snazší je znovu použít. To se může hodit, když například váš objekt Post má atribut author, který ve skutečnosti odkazuje na instanci třídy User. Zatímco normálně factory_girl dokáže odvodit název továrny z názvu asociace, v tomto případě bude marně hledat továrnu author. Použijte tedy alias své továrny user, aby ji bylo možné použít pod aliasovými jmény.
Závislé atributy
Atributy mohou být založeny na hodnotách jiných atributů pomocí vyhodnocovacího nástroje, který je předáván dynamickým blokům atributů:
Přechodné atributy
Může se stát, že váš kód bude DRYed tím, že továrnám předáte přechodné atributy.
Statické a dynamické atributy lze vytvořit jako přechodné atributy. Přechodnéatributy budou v rámci atributu_for ignorovány a nebudou nastaveny v modelu,i když atribut existuje nebo se ho pokusíte přepsat.
V rámci dynamických atributů factory_girl můžete přistupovat k přechodným atributům, jak byste očekávali. Pokud potřebujete přistupovat k vyhodnocovači ve zpětném volání factory_girl,budete muset deklarovat druhý argument bloku (pro vyhodnocovač) a přistupovat k přechodným atributům z něj.
Atributy názvů metod / rezervovaných slov
Pokud vaše atributy kolidují s existujícími metodami nebo rezervovanými slovy, můžete je definovat pomocí add_attribute
.
factory :dna do add_attribute(:sequence) { 'GATTACA' }endfactory :payment do add_attribute(:method) { 'paypal' }end
Dědičnost
Snadno můžete vytvořit více továren pro stejnou třídu bez opakování společných atributů vnořením továren:
Můžete také explicitně přiřadit rodiče:
factory :post do title "A title"endfactory :approved_post, parent: :post do approved trueend
Jak bylo uvedeno výše, je dobrým zvykem definovat základní továrnu pro každou třídu pouze s atributy potřebnými k jejímu vytvoření. Poté vytvořte specifičtějšítovárny, které dědí z tohoto základního rodiče. Definice továren jsou stále kódem, takže je udržujte DRY.
Asociace
V rámci továren je možné nastavit asociace. Pokud je název továrny stejný jako název asociace, lze název továrny vynechat.
factory :post do # ... authorend
Můžete také určit jinou továrnu nebo přepsat atributy:
factory :post do # ... association :author, factory: :user, last_name: "Writely"end
Chování metody asociace se liší v závislosti na použité strategii sestavení rodičovského objektu.
Chcete-li asociovaný objekt neukládat, zadejte v továrně strategii: :build:
Upozorňujeme, že volba strategy: :build
musí být předána explicitnímu volání association
,a nelze ji použít s implicitními asociacemi:
factory :post do # ... author strategy: :build # <<< this does *not* work; causes author_id to be nil
Generování dat pro vztah has_many
je trochu složitější,v závislosti na požadované míře flexibility, ale zde je jistý příklad generování asociovaných dat.
FactoryGirl.define do # post factory with a `belongs_to` association for the user factory :post do title "Through the Looking Glass" user end # user factory without associated posts factory :user do name "John Doe" # user_with_posts will create post data after the user has been created factory :user_with_posts do # posts_count is declared as a transient attribute and available in # attributes on the factory, as well as the callback via the evaluator transient do posts_count 5 end # the after(:create) yields two values; the user instance itself and the # evaluator, which stores all values from the factory, including transient # attributes; `create_list`'s second argument is the number of records # to create and we make sure the user is associated properly to the post after(:create) do |user, evaluator| create_list(:post, evaluator.posts_count, user: user) end end endend
Tímto způsobem můžeme udělat:
create(:user).posts.length # 0create(:user_with_posts).posts.length # 5create(:user_with_posts, posts_count: 15).posts.length # 15
Generování dat pro vztah has_and_belongs_to_many
je velmi podobné výše uvedenému vztahu has_many
, s malou změnou, je třeba předat pole objektů k plurálovému názvu atributu modelu místo jednoho objektu k singulární verzi názvu atributu.
Tady je příklad se dvěma modely, které jsou propojeny prostřednictvímhas_and_belongs_to_many
:
FactoryGirl.define do # language factory with a `belongs_to` association for the profile factory :language do title "Through the Looking Glass" profile end # profile factory without associated languages factory :profile do name "John Doe" # profile_with_languages will create language data after the profile has # been created factory :profile_with_languages do # languages_count is declared as an ignored attribute and available in # attributes on the factory, as well as the callback via the evaluator transient do languages_count 5 end # the after(:create) yields two values; the profile instance itself and # the evaluator, which stores all values from the factory, including # ignored attributes; `create_list`'s second argument is the number of # records to create and we make sure the profile is associated properly # to the language after(:create) do |profile, evaluator| create_list(:language, evaluator.languages_count, profiles: ) end end endend
To nám umožňuje:
Sekvence
Jednotlivé hodnoty ve specifickém formátu (například e-mailové adresy) lze vytvářet pomocí sekvencí. Sekvence se definují voláním sequence
v adefiničním bloku a hodnoty v sekvenci se generují volánímgenerate
:
Sekvence lze použít v dynamických atributech:
factory :invite do invitee { generate(:email) }end
Nebo jako implicitní atributy:
factory :user do email # Same as `email { generate(:email) }`end
A také je možné definovat řádkovou sekvenci, která se používá pouze v určité továrně:
factory :user do sequence(:email) { |n| "person#{n}@example.com" }end
Můžete také přepsat počáteční hodnotu:
factory :user do sequence(:email, 1000) { |n| "person#{n}@example.com" }end
Bez bloku se hodnota inkrementuje sama, počínaje počáteční hodnotou:
factory :post do sequence(:position)end
Sekvence mohou mít také aliasy. Aliasy sekvencí sdílejí stejný čítač:
Definice aliasu a použití výchozí hodnoty (1) pro čítač
factory :user do sequence(:email, aliases: ) { |n| "person#{n}@example.com" }end
Nastavení hodnoty:
factory :user do sequence(:email, 'a', aliases: ) { |n| "person#{n}@example.com" }end
Hodnota musí pouze podporovat metodu #next
. Zde bude další hodnota ‚a‘, pak ‚b‘ atd.
Traity
Traity umožňují seskupit atributy dohromady a pak je použítna libovolnou továrnu.
Traity lze použít jako atributy:
Traity, které definují stejné atributy, nebudou vyvolávat chyby AttributeDefinitionErrors;přednost má trait, který definuje atribut nejpozději.
V podtřídách můžete také přepsat jednotlivé atributy udělené rysem.
Traity lze také předat jako seznam symbolů při konstrukci instance z factory_girl.
Tato schopnost funguje s build
, build_stubbed
, attributes_for
a create
.
create_list
a build_list
Podporovány jsou také metody. Jen nezapomeňte jako druhý parametr předat počet instancí, které chcete vytvořit/vytvořit, jak je zdokumentováno v části „Vytvoření nebo vytvoření více záznamů“ tohoto souboru.
Traity lze snadno použít i s asociacemi:
Pokud používáte názvy asociací, které se liší od továrních:
Traity lze použít v rámci jiných traitů a míchat jejich atributy.
factory :order do trait :completed do completed_at { 3.days.ago } end trait :refunded do completed refunded_at { 1.day.ago } endend
Nakonec mohou traity přijímat přechodné atributy.
Zpětná volání
továrna_girl zpřístupňuje čtyři zpětná volání pro injektování nějakého kódu:
Příklad:
Všimněte si, že v bloku budete mít instanci uživatele. To může být užitečné.
Ve stejné továrně můžete také definovat více druhů zpětných volání:
factory :user do after(:build) { |user| do_something_to(user) } after(:create) { |user| do_something_else_to(user) }end
Továrny mohou také definovat libovolný počet stejných druhů zpětných volání. Tato zpětná volání budou provedena v pořadí, v jakém jsou zadána:
factory :user do after(:create) { this_runs_first } after(:create) { then_this }end
Volání create
vyvolá jak after_build
, tak after_create
zpětné volání.
Stejně jako standardní atributy i podřízené továrny dědí (a mohou také definovat) zpětná volání od své rodičovské továrny.
Pro spuštění bloku lze přiřadit více zpětných volání; to je užitečné při vytváření různých strategií, které spouštějí stejný kód (protože neexistují zpětná volání, která by byla společná pro všechny strategie).
Chcete-li přepsat zpětná volání pro všechny továrny, definujte je v rámci blokuFactoryGirl.define
:
Můžete také volat zpětná volání, která se spoléhají na Symbol#to_proc
:
Modifikace továren
Pokud dostanete sadu továren (například od vývojáře drahokamů), ale chcete je změnit tak, aby lépe vyhovovaly vaší aplikaci, můžete tuto továrnu modifikovat, místo abyste vytvořili podřízenou továrnu a přidali do ní atributy.
Pokud by vám vývojář drahokamu dal továrnu User:
FactoryGirl.define do factory :user do full_name "John Doe" sequence(:username) { |n| "user#{n}" } password "password" endend
Místo vytvoření podřízené továrny, která by přidala další atributy:
Můžete místo toho tuto továrnu upravit.
FactoryGirl.modify do factory :user do full_name "Jane Doe" date_of_birth { 21.years.ago } gender "Female" health 90 endend
Při úpravě továrny můžete změnit libovolné atributy (kromě zpětných volání).
FactoryGirl.modify
musí být volán mimo blok FactoryGirl.define
, protože pracuje s továrnami jinak.
Upozornění: můžete měnit pouze továrny (nikoli sekvence nebo rysy) a zpětná volání se stále skládají jako obvykle. Pokud tedy továrna, kterou upravujete, definuje zpětné volání after(:create)
, definování after(:create)
ji nepřepíše, pouze se spustí po prvním zpětném volání.
Vytváření nebo vytváření více záznamů
Někdy budete chtít vytvořit nebo sestavit více instancí továrny najednou.
built_users = build_list(:user, 25)created_users = create_list(:user, 25)
Tyto metody sestaví nebo vytvoří určitý počet továren a vrátí je jako pole. pro nastavení atributů každé z továren můžete předat hash jako obvykle.
twenty_year_olds = build_list(:user, 25, date_of_birth: 20.years.ago)
build_stubbed_list
vám poskytne plně odříznuté instance:
stubbed_users = build_stubbed_list(:user, 25) # array of stubbed users
K dispozici je také sada metod *_pair
pro vytvoření dvou záznamů najednou:
built_users = build_pair(:user) # array of two built userscreated_users = create_pair(:user) # array of two created users
Pokud potřebujete více hashů atributů, attributes_for_list
je vygeneruje:
users_attrs = attributes_for_list(:user, 25) # array of attribute hashes
Lintování továren
factory_girl umožňuje lintování známých továren:
FactoryGirl.lint
FactoryGirl.lint
vytvoří každou továrnu a zachytí všechny výjimky vyvolanéběhem procesu vytváření. FactoryGirl::InvalidFactoryError
je vyvolán seznam továren (a odpovídajících výjimek) pro továrny, které nemohly být vytvořeny.
Doporučené použití FactoryGirl.lint
je spustit tuto funkci v úlozepřed spuštěním testovací sady.Spuštění v before(:suite)
,bude mít negativní vliv na výkon vašich testůpři spouštění jednotlivých testů.
Příklad úlohy Rake:
Po zavolání FactoryGirl.lint
budete pravděpodobně chtít vymazatdatabázi, protože se pravděpodobně vytvoří záznamy. Výše uvedený příklad používá k vymazání databáze gem database_cleaner; nezapomeňte tento gem přidat do svého souboru gemů pod příslušné skupiny.
Faktory můžete lintovat selektivně tak, že předáte pouze ty, které chcete lintovat:
factories_to_lint = FactoryGirl.factories.reject do |factory| factory.name =~ /^old_/endFactoryGirl.lint factories_to_lint
Tímto způsobem byste lintovali všechny faktory, které nemají předponu old_
.
Lintovat lze také vlastnosti. Tato volba ověřuje, zda každáa každá vlastnost továrny generuje platný objekt sama o sobě.Zapíná se předáním traits: true
metodě lint
:
FactoryGirl.lint traits: true
Tuto funkci lze také kombinovat s dalšími argumenty:
FactoryGirl.lint factories_to_lint, traits: true
Můžete také určit strategii použitou pro lintování:
FactoryGirl.lint strategy: :build
Vlastní konstrukce
Pokud chcete použít factory_girl pro konstrukci objektu, kde jsou některé atributypředávány initialize
, nebo pokud chcete udělat něco jiného než prosté volání new
na vaší konstrukční třídě, můžete přepsat výchozí chování definováním initialize_with
na vaší továrně. Příklad:
Ačkoli je factory_girl napsána pro práci s ActiveRecord out of box, může také pracovat s jakoukoli třídou Ruby. Pro maximální kompatibilitu s ActiveRecord,výchozí inicializátor sestaví všechny instance voláním new
na vaší třídě factorybez jakýchkoli argumentů. Poté zavolá metody atributového zapisovatele, aby přiřadil všechny hodnoty atributů. Zatímco pro ActiveRecord to funguje dobře, pro téměř jakoukoli jinou třídu Ruby to ve skutečnosti nefunguje.
Inicializátor můžete přepsat, aby:
- Vytvořit objekty jiné než ActiveRecord, které vyžadují argumenty
initialize
- Použít jinou metodu než
new
k instanciaci instance - Dělat šílené věci, jako je zdobení instance po jejím vytvoření
Při použití initialize_with
nemusíte při volání new
deklarovat samotnou třídu; nicméně všechny ostatní metody třídy, které chcete volat, budou muset být volány explicitně na třídě.
Například:
factory :user do name "John Doe" initialize_with { User.build_with_name(name) }end
V rámci bloku initialize_with
můžete také přistupovat ke všem veřejným atributům voláním attributes
:
factory :user do transient do 5 end name "John Doe" initialize_with { new(attributes) }end
Tím se vytvoří hash všech atributů, který se předá do new
. Nebude zahrnovat přechodné atributy, ale vše ostatní definované v továrně bude předáno (asociace, vyhodnocené sekvence atd.)
Můžete definovat initialize_with
pro všechny továrny tak, že jej zahrnete do blokuFactoryGirl.define
:
FactoryGirl.define do initialize_with { new("Awesome first argument") }end
Při použití initialize_with
se atributy, k nimž se přistupuje z bloku initialize_with
, přiřazují pouze v konstruktoru; to odpovídá zhruba následujícímu kódu:
FactoryGirl.define do factory :user do initialize_with { new(name) } name { 'value' } endendbuild(:user)# runsUser.new('value')
Tím se zabrání duplicitnímu přiřazení; ve verzích factory_girl před 4. verzí se atributy přiřazují pouze v konstruktoru.0 by to probíhalo takto:
Vlastní strategie
V některých případech můžete chtít rozšířit chování factory_girl přidáním vlastní strategie sestavení.
Strategie definují dvě metody: association
a result
. association
přijímá instanci FactoryGirl::FactoryRunner
, na kterou můžete zavolat run
a případně strategii přepsat. Druhá metoda, result
,přijímá instanci FactoryGirl::Evaluation
. Poskytuje způsob, jak spustit zpětné volání (pomocí notify
), object
nebo hash
(pro získání výsledné instance nebo ahash na základě atributů definovaných v factory) a create
, kterývykoná zpětné volání to_create
definované na factory.
Chcete-li pochopit, jak factory_girl interně používá strategie, je asi nejjednodušší prostě si prohlédnout zdrojový kód každé ze čtyř výchozích strategií.
Tady je příklad sestavení strategie pomocíFactoryGirl::Strategy::Create
pro vytvoření reprezentace JSON modelu.
Aby factory_girl rozpoznala novou strategii, můžete ji zaregistrovat:
FactoryGirl.register_strategy(:json, JsonStrategy)
To vám umožní volat
FactoryGirl.json(:user)
Nakonec můžete přepsat vlastní strategie factory_girl, pokud chcete, zaregistrováním nového objektu místo strategií.
Vlastní zpětná volání
Vlastní zpětná volání můžete definovat, pokud používáte vlastní strategie:
Vlastní metody pro perzistenci objektů
Ve výchozím nastavení se při vytváření záznamu zavolá save!
na instanci; protože to nemusí být vždy ideální, můžete toto chování přepsat definicíto_create
na továrně:
factory :different_orm_model do to_create { |instance| instance.persist! }end
Chcete-li metodu perzistence při vytváření úplně zakázat, můžete skip_create
pro tuto továrnu:
factory :user_without_database do skip_createend
Chcete-li přepsat to_create
pro všechny továrny, definujte ji v blokuFactoryGirl.define
:
FactoryGirl.define do to_create { |instance| instance.persist! } factory :user do name "John Doe" endend
ActiveSupport Instrumentation
Aby bylo možné sledovat, jaké továrny jsou vytvářeny (a s jakou strategií sestavení),ActiveSupport::Notifications
jsou zahrnuty, aby poskytovaly způsob, jak se přihlásit ke spuštění továren. Jedním z příkladů by bylo sledování faktorů na základě prahové hodnoty času provedení.
Dalším příkladem by bylo sledování všech faktorů a jejich použití v celé sadě testů. Pokud používáte RSpec, je to stejně jednoduché jako přidání before(:suite)
a after(:suite)
:
Rails Preloadery a RSpec
Při spuštění RSpec s Rails preloaderem, jako je spring
nebo zeus
, je možnépři vytváření faktoru s asociacemi narazit na chybu ActiveRecord::AssociationTypeMismatch
, jak je uvedeno níže:
K chybě dojde během běhu sady testů:
Dvě možná řešení jsou buď spustit sadu bez předzavaděče, nebo přidat FactoryGirl.reload
do konfigurace RSpec, například takto:
RSpec.configure do |config| config.before(:suite) { FactoryGirl.reload }end
Používání bez Bundleru
Pokud Bundler nepoužíváte, ujistěte se, že máte gem nainstalovaný a zavoláte:
require 'factory_girl'
Pokud je vyžadován, za předpokladu, že máte adresářovou strukturu spec/factories
nebotest/factories
, stačí spustit
FactoryGirl.find_definitions
Pokud pro své továrny používáte samostatnou adresářovou strukturu, můžete před pokusem o nalezení definic změnit cesty k souborům s definicemi:
FactoryGirl.definition_file_paths = %w(custom_factories_directory)FactoryGirl.find_definitions
Pokud nemáte samostatný adresář s továrnami a chcete je definovat inline, je to také možné:
require 'factory_girl'FactoryGirl.define do factory :user do name 'John Doe' date_of_birth { 21.years.ago } endend
.