- Update Your Gemfile
- Konfiguráld a tesztcsomagodat
- Test::Unit
- Cucumber
- Spinach
- Minitest
- Minitest::Spec
- minitest-rails
- Gyárak definiálása
- Gyárak használata
- Dinamikus attribútumok
- Aliasok
- Függő attribútumok
- Tranziens attribútumok
- Módszer neve / Fenntartott szó attribútumok
- Öröklés
- Társítások
- Sorozatok
- Traits
- Callbacks
- Gyárak módosítása
- Elkészítés vagy több rekord létrehozása
- Linting Factories
- Custom Construction
- Egyéni stratégiák
- Egyedi visszahívások
- Az objektumok perzisztenciájának egyéni módszerei
- ActiveSupport Instrumentation
- Rails előbetöltők és RSpec
- Bundler nélkül használva
Update Your Gemfile
Ha Rails-t használ, akkor a factory_girl_rails
:
gem "factory_girl_rails", "~> 4.0"
Ha nem használ Rails-t, akkor csak a factory_girl
:
gem "factory_girl", "~> 4.0"
export JRUBY_OPTS=--1.9
Amikor a Gemfile frissítve van, akkor a csomagot is frissíteni kell.
Konfiguráld a tesztcsomagodat
Ne felejtsd el megkövetelni a fenti fájlt a rails_helperedben, mivel a support mappa nem töltődik be buzgón
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
Ha a tesztcsomagodban nem szerepel a FactoryGirl::Syntax::Methods
, akkor minden factory_girl metódus elé FactoryGirl
kell írni.
Gyárak definiálása
Minden gyárnak van egy neve és egy attribútumkészlete. A név alapján alapértelmezés szerint kitaláljuk az objektum osztályát, de lehetőség van annak explicit megadására:
Kifejezetten ajánlott, hogy minden osztályhoz legyen egy gyár, amely az adott osztály példányának létrehozásához szükséges legegyszerűbb attribútumkészletet biztosítja. Ha ActiveRecord objektumokat hoz létre, ez azt jelenti, hogy csak olyan attribútumokat adjon meg, amelyek az érvényesítések révén szükségesek, és amelyeknek nincsenek alapértelmezett értékei. Örökléssel további gyárak hozhatók létre az egyes osztályok általános forgatókönyveinek lefedésére.
Az azonos nevű gyárak többszörös definiálásának kísérlete hibát fog okozni.
A gyárak bárhol definiálhatók, de automatikusan betöltődnek a FactoryGirl.find_definitions
meghívása után, ha a gyárak a következő helyeken lévő fájlokban vannak definiálva:
test/factories.rbspec/factories.rbtest/factories/*.rbspec/factories/*.rb
Gyárak használata
A factory_girl többféle build stratégiát támogat: build, create, attributes_for és build_stubbed:
Nem számít, hogy melyik stratégiát használjuk, a definiált attribútumokat egy hash átadásával felül lehet írni:
# Build a User instance and override the first_name propertyuser = build(:user, first_name: "Joe")user.first_name# => "Joe"
Dinamikus attribútumok
A legtöbb gyári attribútumot statikus értékekkel lehet hozzáadni, amelyeket a gyár definiálásakor értékelnek ki, de néhány attribútumhoz (például az asszociációkhoz és más olyan attribútumokhoz, amelyeket dinamikusan kell generálni) minden egyes példány generálásakor értéket kell rendelni. Ezeket a “dinamikus” attribútumokat paraméter helyett egy blokk átadásával lehet hozzáadni:
factory :user do # ... activation_code { User.generate_activation_code } date_of_birth { 21.years.ago }end
A Ruby blokkszintaxisa miatt az attribútumok Hash
es formában történő definiálása (például a sorializált/JSON oszlopok esetében) két szögletes zárójelet igényel:
factory :program do configuration { { auto_resolve: false, auto_define: true } }end
Aliasok
Afactory_girl lehetővé teszi, hogy aliasokat definiáljunk meglévő gyárakhoz, hogy megkönnyítsük azok újrafelhasználását. Ez akkor jöhet jól, ha például a Post objektumodnak van egy author attribútuma, amely valójában egy User osztály egy példányára utal. Míg normál esetben a factory_girl a gyár nevére az asszociáció nevéből tud következtetni, ebben az esetben hiába keres egy szerzői gyárat. Tehát aliasítsd a user factory-t, hogy az alias nevek alatt is használható legyen.
Függő attribútumok
Az attribútumok más attribútumok értékein alapulhatnak a dinamikus attribútumblokkokhoz kiadott evaluatort segítségével:
Tranziens attribútumok
Elképzelhető, hogy a kódodat DRYDEzheted azzal, hogy tranziens attribútumokat adsz át a gyáraknak.
A statikus és dinamikus attribútumok létrehozhatók tranziens attribútumokként. A tranziens attribútumok figyelmen kívül maradnak az attributes_for-on belül, és nem lesznek beállítva a modellben,még akkor sem, ha az attribútum létezik, vagy ha megpróbálod felülírni.
A factory_girl dinamikus attribútumain belül a tranziens attribútumokhoz úgy férhetsz hozzá, ahogyan az elvárható. Ha egy factory_girl visszahívásban kell elérnie az értékelőt,akkor egy második blokk argumentumot kell deklarálnia (az értékelőnek) és onnan kell elérnie a tranziens attribútumokat.
Módszer neve / Fenntartott szó attribútumok
Ha az attribútumai ütköznek meglévő metódusokkal vagy fenntartott szavakkal, akkor a add_attribute
segítségével definiálhatja őket.
factory :dna do add_attribute(:sequence) { 'GATTACA' }endfactory :payment do add_attribute(:method) { 'paypal' }end
Öröklés
A gyárak egymásba ágyazásával könnyen létrehozhat több gyárat ugyanahhoz az osztályhoz a közös attribútumok ismétlése nélkül:
A szülőt explicit módon is hozzárendelheti:
factory :post do title "A title"endfactory :approved_post, parent: :post do approved trueend
Amint fentebb említettük, jó gyakorlat, hogy minden osztályhoz definiáljon egy alap gyárat,amely csak a létrehozásához szükséges attribútumokat tartalmazza. Ezután hozzon létre specifikusabb gyárakat, amelyek ebből az alapszülőből öröklődnek. A gyárdefiníciók még mindig kódot jelentenek, ezért tartsuk őket DRY.
Társítások
A gyárakon belül lehetséges társításokat létrehozni. Ha a gyár neve megegyezik az asszociáció nevével, a gyár neve elhagyható.
factory :post do # ... authorend
Egy másik gyárat vagy felülbíráló attribútumokat is megadhatunk:
factory :post do # ... association :author, factory: :user, last_name: "Writely"end
Az asszociációs metódus viselkedése a szülőobjektumhoz használt építési stratégiától függően változik.
Hogy ne mentsük el a társított objektumot, adjuk meg a stratégiát: :build a gyárban:
Megjegyezzük, hogy a strategy: :build
opciót át kell adni a association
explicit hívásához,és nem használható implicit társításoknál:
factory :post do # ... author strategy: :build # <<< this does *not* work; causes author_id to be nil
A has_many
kapcsolat adatainak generálása egy kicsit bonyolultabb,a kívánt rugalmasság mértékétől függően, de itt egy tuti példa a társított adatok generálására.
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
Ez lehetővé teszi számunkra:
create(:user).posts.length # 0create(:user_with_posts).posts.length # 5create(:user_with_posts, posts_count: 15).posts.length # 15
Az adatok generálása egy has_and_belongs_to_many
kapcsolathoz nagyon hasonló a fenti has_many
kapcsolathoz, egy apró változtatással, a modell többes számú attribútumnevéhez egy objektumsorozatot kell átadni, nem pedig egyetlen objektumot az attribútumnév egyes számú változatához.
Itt egy példa két modellel, amelyek ahas_and_belongs_to_many
révén kapcsolódnak:
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
Ez lehetővé teszi számunkra:
Sorozatok
Egyedi értékek egy adott formátumban (például e-mail címek) szekvenciák segítségével generálhatók. A szekvenciákat a sequence
hívásával definiáljuk a definiálási blokkban, és a szekvenciában lévő értékeket agenerate
hívásával generáljuk:
A szekvenciák dinamikus attribútumokban használhatók:
factory :invite do invitee { generate(:email) }end
Vagy implicit attribútumokként:
factory :user do email # Same as `email { generate(:email) }`end
És lehetséges olyan soron belüli szekvencia definiálása is, amely csak egy adott gyárban használatos:
factory :user do sequence(:email) { |n| "person#{n}@example.com" }end
A kezdeti értéket is felülbírálhatjuk:
factory :user do sequence(:email, 1000) { |n| "person#{n}@example.com" }end
Blokk nélkül az érték önmagát növeli, a kezdeti értéktől kezdve:
factory :post do sequence(:position)end
A szekvenciáknak lehetnek aliasai is. A szekvencia aliasok ugyanazt a számlálót használják:
Aliasok definiálása és alapértelmezett érték (1) használata a számlálóhoz
factory :user do sequence(:email, aliases: ) { |n| "person#{n}@example.com" }end
Az érték beállítása:
factory :user do sequence(:email, 'a', aliases: ) { |n| "person#{n}@example.com" }end
Az értéknek csak a #next
módszert kell támogatnia. Itt a következő érték ‘a’ lesz, majd ‘b’, stb.
Traits
A traits lehetővé teszi az attribútumok csoportosítását, majd azok alkalmazását bármelyik gyárra.
A traits attribútumként használható:
Az azonos attribútumokat definiáló traits nem fog AttributeDefinitionErrors-t kiváltani;az attribútumot legkésőbb definiáló trait kap elsőbbséget.
A trait által biztosított egyes attribútumokat alosztályokban is felülbírálhatod.
A traiteket szimbólumok listájaként is átadhatod, amikor a factory_girl-ből konstruálsz egy példányt.
Ez a képesség működik a build
, build_stubbed
, attributes_for
és create
metódusokkal.
create_list
és build_list
metódusok is támogatottak. Csak ne felejtsd el átadni a létrehozandó/építendő példányok számát második paraméterként, ahogyan azt a “Több rekord építése vagy létrehozása” című részben dokumentáltuk.
A tulajdonságok könnyen használhatók asszociációkkal is:
Ha a gyáritól eltérő asszociációs neveket használsz:
A tulajdonságok más tulajdonságokon belül is használhatók, hogy keverjük az attribútumaikat.
factory :order do trait :completed do completed_at { 3.days.ago } end trait :refunded do completed refunded_at { 1.day.ago } endend
Végül a vonások átmeneti attribútumokat is elfogadhatnak.
Callbacks
A factory_girl négy callbacket tesz elérhetővé némi kód befecskendezéséhez:
Példák:
Megjegyezzük, hogy a blokkban a felhasználó egy példánya lesz. Ez hasznos lehet.
Egy gyárban többféle visszahívást is definiálhatunk:
factory :user do after(:build) { |user| do_something_to(user) } after(:create) { |user| do_something_else_to(user) }end
A gyárakban is tetszőleges számú azonos típusú visszahívást definiálhatunk. Ezek a visszahívások a megadott sorrendben kerülnek végrehajtásra:
factory :user do after(:create) { this_runs_first } after(:create) { then_this }end
A create
hívása mind a after_build
, mind a after_create
visszahívást meg fogja hívni.
A standard attribútumokhoz hasonlóan a gyermek gyárak is örökölnek (és definiálhatnak is) visszahívásokat a szülő gyáruktól.
Egy blokk futtatásához több visszahívás is hozzárendelhető; ez akkor hasznos, ha különböző stratégiákat építünk, amelyek ugyanazt a kódot futtatják (mivel nincsenek olyan visszahívások, amelyek minden stratégiában közösek).
Az összes gyárhoz tartozó visszahívások felülbírálásához definiáljuk őket aFactoryGirl.define
blokkban:
A Symbol#to_proc
-re támaszkodó visszahívásokat is meghívhatjuk:
Gyárak módosítása
Ha kapunk egy sor gyárat (mondjuk egy gem fejlesztőtől), de szeretnénk megváltoztatni őket, hogy jobban illeszkedjenek az alkalmazásunkhoz, akkor egy gyermek gyár létrehozása és az attribútumok hozzáadása helyett módosíthatjuk azt a gyárat.
Ha egy gem egy User factory-t ad:
FactoryGirl.define do factory :user do full_name "John Doe" sequence(:username) { |n| "user#{n}" } password "password" endend
Ahelyett, hogy létrehozna egy gyermek factory-t, amely további attribútumokat ad hozzá:
Módosíthatja ezt a factory-t.
FactoryGirl.modify do factory :user do full_name "Jane Doe" date_of_birth { 21.years.ago } gender "Female" health 90 endend
A factory módosítása során bármelyik attribútumot megváltoztathatja (a visszahívásokon kívül).
FactoryGirl.modify
egy FactoryGirl.define
blokkon kívül kell meghívni, mivel ez másképp működik a gyárakkal.
Egy figyelmeztetés: csak gyárakat módosíthatsz (szekvenciákat vagy vonásokat nem), és a visszahívások továbbra is a szokásos módon összetartoznak. Tehát, haa módosítandó gyár definiál egy after(:create)
visszahívást, akkor egy after(:create)
definiálása nem fogja felülírni azt, csak az első visszahívás után fog lefutni.
Elkészítés vagy több rekord létrehozása
Néha egyszerre több példányt akarsz létrehozni vagy létrehozni egy gyárból.
built_users = build_list(:user, 25)created_users = create_list(:user, 25)
Ezek a metódusok meghatározott számú gyárat építenek vagy hoznak létre, és egy tömbként adják vissza őket.Az egyes gyárak attribútumainak beállításához átadhatunk egy hash-t, ahogyan általában szoktuk.
twenty_year_olds = build_list(:user, 25, date_of_birth: 20.years.ago)
build_stubbed_list
teljesen kimerevített példányokat ad:
stubbed_users = build_stubbed_list(:user, 25) # array of stubbed users
Egy sor *_pair
metódus is van, amelyekkel egyszerre két rekordot hozhatunk létre:
built_users = build_pair(:user) # array of two built userscreated_users = create_pair(:user) # array of two created users
Ha több attribútumhashre van szükségünk, a attributes_for_list
létrehozza azokat:
users_attrs = attributes_for_list(:user, 25) # array of attribute hashes
Linting Factories
factory_girl lehetővé teszi az ismert gyárak lintingelését:
FactoryGirl.lint
FactoryGirl.lint
létrehozza az egyes gyárakat és elkapja a létrehozás során felmerülő kivételeket. A FactoryGirl::InvalidFactoryError
előhívja a gyárak listáját (és a megfelelő kivételeket) azon gyárak esetében, amelyeket nem sikerült létrehozni.
A FactoryGirl.lint
ajánlott használata az, hogy a tesztcsomag végrehajtása előtt futtassa ezt egy feladatban.A before(:suite)
-ben való futtatás negatívan befolyásolja a tesztek teljesítményét,amikor egyes teszteket futtatunk.
Példa Rake feladatra:
A FactoryGirl.lint
meghívása után valószínűleg ki kell üríteni azadatbázist, mivel valószínűleg rekordok jönnek létre. A fenti példa a database_cleaner gem-et használja az adatbázis törléséhez; győződjön meg róla, hogy a gem-et hozzáadja a Gemfile-hoz a megfelelő csoportok alatt.
A gyárakat szelektíven is szedheti, ha csak azokat a gyárakat adja át, amelyeket szedni akar:
factories_to_lint = FactoryGirl.factories.reject do |factory| factory.name =~ /^old_/endFactoryGirl.lint factories_to_lint
Ez minden olyan gyárat szed, amelyeknek nincs old_
előtagja.
A tulajdonságok is szedhetők. Ez az opció ellenőrzi, hogy egy gyár minden egyes tulajdonsága önmagában is érvényes objektumot generál.Ezt a traits: true
átadásával kapcsolhatjuk be a lint
metódusnak:
FactoryGirl.lint traits: true
Ez más argumentumokkal is kombinálható:
FactoryGirl.lint factories_to_lint, traits: true
Megadhatjuk a lintinghez használt stratégiát is:
FactoryGirl.lint strategy: :build
Custom Construction
Ha a factory_girl-t akarod használni egy olyan objektum létrehozására, ahol néhány attribútumot átadsz a initialize
-nek, vagy ha valami mást akarsz csinálni, mint egyszerűen a new
hívását a build osztályodon, akkor felülbírálhatod az alapértelmezett viselkedést a initialize_with
definiálásával a gyáradon. Példa:
Bár a factory_girl úgy van megírva, hogy a dobozból ActiveRecorddal működjön, bármilyen Ruby osztállyal is működhet. Az ActiveRecorddal való maximális kompatibilitás érdekében,az alapértelmezett inicializáló az összes példányt úgy építi fel, hogy a new
-t hívja meg az építő osztályodon minden argumentum nélkül. Ezután meghívja az attribútum író metódusokat, hogy hozzárendelje az összesattribútum értékét. Míg ez az ActiveRecord esetében jól működik, addig szinte semmilyen más Ruby osztály esetében nem működik.
Az inicializálót felülbírálhatja, hogy:
- Elkészíteni olyan nem ActiveRecord objektumokat, amelyekhez
initialize
- A
new
-tól eltérő metódust használjon a példány instanciálásához - Tegyen olyan őrült dolgokat, mint például a példány díszítése a felépítés után
A initialize_with
használatakor nem kell magát az osztályt deklarálnia a new
hívásakor; azonban minden más osztály metódust, amit meg akarsz hívni, explicit módon meg kell hívni az osztályon.
Például:
factory :user do name "John Doe" initialize_with { User.build_with_name(name) }end
A initialize_with
blokkban az összes nyilvános attribútumot elérhetjük a attributes
meghívásával:
factory :user do transient do 5 end name "John Doe" initialize_with { new(attributes) }end
Ez létrehoz egy hash-t az összes attribútumból, amelyet át kell adni a new
-nak. Nem tartalmazza a tranziens attribútumokat, de minden más, a gyárban definiált attribútumot átad (asszociációk, kiértékelt szekvenciák stb.)
A initialize_with
minden gyár számára definiálható, ha aFactoryGirl.define
blokkba beillesztjük:
FactoryGirl.define do initialize_with { new("Awesome first argument") }end
A initialize_with
használatakor a initialize_with
blokkból elérhető attribútumok csak a konstruktorban kerülnek hozzárendelésre; ez nagyjából a következő kódnak felel meg:
FactoryGirl.define do factory :user do initialize_with { new(name) } name { 'value' } endendbuild(:user)# runsUser.new('value')
Ez megakadályozza a duplikált hozzárendelést; a factory_girl 4. előtti verzióiban.0-ban ez így futna:
Egyéni stratégiák
Vannak esetek, amikor a factory_girl viselkedését egy egyéni építési stratégia hozzáadásával szeretné bővíteni.
A stratégiák két módszert definiálnak: association
és result
. A association
kap egy FactoryGirl::FactoryRunner
példányt, amire meghívhatod arun
, felülírva a stratégiát, ha akarod. A második metódus, result
,egy FactoryGirl::Evaluation
példányt fogad. Lehetőséget biztosít a visszahívások kiváltására (a notify
segítségével), object
vagy hash
(az eredménypéldány vagy ahash kinyerésére a gyárban definiált attribútumok alapján), és create
, amelyvégrehajtja a gyárban definiált to_create
visszahívást.
Hogy megértsük, hogyan használja a factory_girl a stratégiákat belsőleg, valószínűleg a legegyszerűbb, ha csak a négy alapértelmezett stratégia mindegyikének a forrását nézzük meg.
Itt egy példa egy stratégia összeállítására aFactoryGirl::Strategy::Create
segítségével, hogy felépítsük a modell JSON reprezentációját.
Hogy a factory_girl felismerje az új stratégiát, regisztrálhatja:
FactoryGirl.register_strategy(:json, JsonStrategy)
Ez lehetővé teszi, hogy meghívja
FactoryGirl.json(:user)
Végül, ha szeretné, felülírhatja a factory_girl saját stratégiáit egy új objektum regisztrálásával a stratégiák helyett.
Egyedi visszahívások
Egyedi visszahívások definiálhatók, ha egyéni stratégiákat használ:
Az objektumok perzisztenciájának egyéni módszerei
Egy rekord létrehozása alapértelmezés szerint save!
meghívja a példányt; mivel ez nem mindig ideális, felülbírálhatja ezt a viselkedést a gyáron történő to_create
meghatározással:
factory :different_orm_model do to_create { |instance| instance.persist! }end
A perzisztencia-módszer teljes kikapcsolásához a létrehozáskor skip_create
az adott gyárra vonatkozóan:
factory :user_without_database do skip_createend
Az összes gyárra vonatkozó to_create
felülbírálásához definiáld aFactoryGirl.define
blokkban:
FactoryGirl.define do to_create { |instance| instance.persist! } factory :user do name "John Doe" endend
ActiveSupport Instrumentation
Azért, hogy nyomon követhető legyen, milyen gyárakat hoznak létre (és milyen build stratégiával),ActiveSupport::Notifications
azért van benne, hogy módot adjon a futtatott gyárakra való feliratkozásra. Egy példa lehet a gyárak követése a végrehajtási idő küszöbértéke alapján.
Egy másik példa lehet az összes gyár követése és annak nyomon követése, hogy hogyan használják őket a tesztkészleten keresztül. Ha RSpec-et használ, ez olyan egyszerű, mint egybefore(:suite)
és after(:suite)
hozzáadása:
Rails előbetöltők és RSpec
Az RSpec futtatásakor egy Rails előbetöltővel, mint például spring
vagy zeus
, előfordulhat, hogy ActiveRecord::AssociationTypeMismatch
hiba lép fel, amikor egy gyárat hoz létre asszociációkkal, mint alább:
A hiba a tesztcsomag futtatása során lép fel:
A két lehetséges megoldás az, hogy vagy futtatjuk a csomagot az előtöltő nélkül, vagy hozzáadjuk a FactoryGirl.reload
-t az RSpec konfigurációjához, például így:
RSpec.configure do |config| config.before(:suite) { FactoryGirl.reload }end
Bundler nélkül használva
Ha nem használ Bundler-t, győződjön meg róla, hogy a gem telepítve van, és hívja meg:
require 'factory_girl'
Amikor szükséges, feltételezve, hogy a könyvtárszerkezet spec/factories
vagytest/factories
, csak annyit kell tennie, hogy futtatja
FactoryGirl.find_definitions
Ha külön könyvtárszerkezetet használ a gyárakhoz, akkor megváltoztathatja a definíciós fájlok elérési útvonalát, mielőtt megpróbálja megtalálni a definíciókat:
FactoryGirl.definition_file_paths = %w(custom_factories_directory)FactoryGirl.find_definitions
Ha nincs külön könyvtárad a gyáraknak, és inline szeretnéd definiálni őket, az is lehetséges:
require 'factory_girl'FactoryGirl.define do factory :user do name 'John Doe' date_of_birth { 21.years.ago } endend
.