Factory girl

21
factory_girl

Transcript of Factory girl

Page 1: Factory girl

factory_girl

Page 2: 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.

Page 3: Factory girl

● 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.

Page 4: Factory girl

# 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

Page 5: Factory girl

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 instanceattrs = attributes_for(:user)

# Returns an object with all defined attributes stubbed outstub = build_stubbed(:user)

# Passing a block to any of the methods above will yield the return objectcreate(:user) do |user|

user.posts.create(attributes_for(:post))end

Page 6: Factory girl

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

Page 7: Factory girl

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

Page 8: Factory girl

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]"

Page 9: Factory girl

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

Page 10: Factory girl

● 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.

Page 11: Factory girl

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

emailend

➢ Or in lazy attributes:

factory :invite doinvitee { generate(:email) }

end

Page 12: Factory girl

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

Page 13: Factory girl

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

Page 14: Factory girl

● 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.

Page 15: Factory girl

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"

Page 16: Factory girl

● 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.

Page 17: Factory girl

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.

Page 18: Factory girl

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

Page 19: Factory girl

Wrapping up...

● FactoryGirl is Magic

● Easier to maintain

● Less of Headache

● Fun to work with

Page 20: Factory girl

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/

Page 21: Factory girl