Factory girl

swatijadhav351 675 views 21 slides Feb 16, 2015
Slide 1
Slide 1 of 21
Slide 1
1
Slide 2
2
Slide 3
3
Slide 4
4
Slide 5
5
Slide 6
6
Slide 7
7
Slide 8
8
Slide 9
9
Slide 10
10
Slide 11
11
Slide 12
12
Slide 13
13
Slide 14
14
Slide 15
15
Slide 16
16
Slide 17
17
Slide 18
18
Slide 19
19
Slide 20
20
Slide 21
21

About This Presentation

factory_girl is a fixtures replacement with a straightforward definition syntax, support for multiple build strategies (saved instances, unsaved instances, attribute hashes, and stubbed objects), and support for multiple factories for the same class, including factory inheritance.


Slide Content

factory_girl

# RSpec # spec/support/factory_girl.rb RSpec .configure do |config| config. include FactoryGirl :: Syntax ::Methods end If you do not include FactoryGirl::Syntax::Methods in your test suite, then all factory_girl methods will need to be prefaced with FactoryGirl.

One factory for each class that provides the simplest set of attributes necessary to create an instance of that class Provide attributes that are required through validations and that do not have defaults. Other factories can be created through inheritance to cover common scenarios for each class. Attempting to define multiple factories with the same name will raise an error.

# This will guess the User class FactoryGirl .define do factory :user do first_name "John" last_name "Doe" admin false end # This will use the User class (Admin would have been guessed) factory :admin , class: User do first_name "Admin" last_name "User" admin true end end

Using Factories user = build( :user ) # Returns a User instance that's not saved user = create( :user ) # Returns a saved User instance user = build( :user , first_name: "Joe" ) # Build a User instance and override the first_name property # Returns a hash of attributes that can be used to build a User instance attrs = attributes_for( :user ) # Returns an object with all defined attributes stubbed out stub = build_stubbed( :user ) # Passing a block to any of the methods above will yield the return object create( :user ) do |user| user.posts.create(attributes_for( :post )) end

Lazy Attributes factory attributes can be added using static values that are evaluated when the factory is defined. Some attributes will need values assigned each time an instance is generated. These "lazy" attributes can be added by passing a block instead of a parameter. factory :user do # ... activation_code { User .generate_activation_code } date_of_birth { 21 .years.ago } end

Aliases factory :user , aliases: [ :author , :commenter ] do first_name "John" last_name "Doe" end factory :post do author # instead of # association :author, factory: :user title "How to read a book effectively" end # Post Model class Post belongs_to :author, class: User end

Dependent Attributes Attributes can be based on the values of other attributes using the evaluator that is yielded to lazy attribute blocks: factory :user do first_name "Joe" last_name "Blow" email { "#{ first_name }.#{ last_name }@example.com" .downcase } end create( :user , last_name: "Doe" ).email # => "[email protected]"

Inheritance You can easily create multiple factories for the same class without repeating common attributes by nesting factories. factory :post do title "A title" factory :approved_post do approved true end end create( :approved_post ) factory :post do title "A title" end factory :approved_post , parent: :post do approved true end

It's good practice to define a basic factory for each class with only the attributes required to create it. Create more specific factories that inherit from this basic parent. Factory definitions are still code, so keep them DRY.

Sequences Unique values in a specific format can be generated using sequences. Sequences are defined by calling sequence in a definition block, and values in a sequence are generated by calling generate . # Defines a new sequence FactoryGirl .define do sequence :email do |n| "person#{ n }@example.com" end end generate :email Sequences can be used as attributes: factory :user do email end Or in lazy attributes: factory :invite do invitee { generate( :email ) } end

Building or Creating Multiple Records create or build multiple instances of a factory at once. built_users = build_list( :user , 25 ) built_users = build_stubbed_list( :user , 25 ) created_users = create_list( :user , 25, date_of_birth : 12.years.ago ) These methods will build or create a specific amount of factories and return them as an array. There's also a set of *_pair methods for creating two records at a time: built_users = build_pair( :user ) # array of two built users created_users = create_pair( :user ) # array of two created users

Callbacks after(:build) - called after a factory is built (via FactoryGirl.build, FactoryGirl.create) before(:create) - called before a factory is saved (via FactoryGirl.create) after(:create) - called after a factory is saved (via FactoryGirl.create) after(:stub) - called after a factory is stubbed (via FactoryGirl.build_stubbed) # Define a factory that calls the generate_hashed_password method after it is built factory :user do after( :build ) { |user| generate_hashed_password(user) } end

you'll have an instance of the user in the block. You can also define multiple types of callbacks on the same factory factory :user do after( :build ) { |user| do_something_to(user) } after( :create ) { |user| do_something_else_to(user) } end Factories can also define any number of the same kind of callback. These callbacks will be executed in the order they are specified: Calling create will invoke both after_build and after_create callbacks . Child factories will inherit (and can also define) callbacks from their parent factory.

Transient Attributes factory :user do transient do rockstar true upcased false end name { "John Doe#{" - Rockstar" if rockstar }" } after( :create ) do |user, evaluator| user.name.upcase! if evaluator.upcased end end create( :user , upcased: true ).name #=> "JOHN DOE - ROCKSTAR"

Static and dynamic attributes can be created as transient attributes. Transient attributes will be ignored within attributes_for. Within factory_girl's dynamic attributes, you can access transient attributes as you would expect. If you need to access the evaluator in a factory_girl callback, you'll need to declare a second block argument (for the evaluator) and access transient attributes from there.

Traits Traits allow you to group attributes together and then apply them to any factory. Traits can be used as attributes: Traits that define the same attributes won't raise AttributeDefinitionErrors; the trait that defines the attribute latest gets precedence. You can also override individual attributes granted by a trait in subclasses. Traits can also be passed in as a list of symbols when you construct an instance from factory_girl. Traits can be used with associations easily too: Traits can be used within other traits to mix in their attributes.

factory :user do name "Friendly User" login { name } trait :admin do is_admin true login { "#{ name } (Admin)" } end end factory :male_admin, factory: :user do admin gender “Male” end factory :post do association :author , :admin , factory: :user , name: 'John Doe' end

Wrapping up... FactoryGirl is Magic Easier to maintain Less of Headache Fun to work with

References.. http://robots.thoughtbot.com/use-factory-girls-build-stubbed-for-a-faster-test http://www.agileventures.org/articles/testing-with-rspec-stubs-mocks-factories-what-to-choose https://github.com/thoughtbot/factory_girl/blob/master/GETTING_STARTED.md part 1: http://cookieshq.co.uk/posts/useful-factory-girl-methods/ part 2: http://cookieshq.co.uk/posts/useful-factory-girl-methods-part-two/