Skip to content

Instantly share code, notes, and snippets.

@EliseFitz15
Last active April 2, 2020 17:10
Show Gist options
  • Save EliseFitz15/b0a0624fafd1df6ae2ca to your computer and use it in GitHub Desktop.
Save EliseFitz15/b0a0624fafd1df6ae2ca to your computer and use it in GitHub Desktop.
factory_girl_sinatra.md

#Factory Girl Clinic

FactoryGirl is a gem that helps you with testing. It basically allows you to create "factories" that crank out objects to your database for you to test. Once FactoryGirl is set up, instead of needing to type this:

let(:pokemon) { Pokemon.new(name: "Onyx", ability: "Rock Smash", poketype: "Earth", strength: 50, age: 2, pokemaster: pokemaster) }

I can type something like this:

let(:pokemon) { FactoryGirl.create(:pokemon) }

##Set Up in Sinatra

 git clone https://github.com/EliseFitz15/factory_girl_sinatra_setup <YOUR_APP_NAME>
 cd <YOUR_APP_NAME>
 bundle install
 rm -rf .git && git init && git add -A && git commit -m 'Initial commit'
  • First, add Factory Girl to your Gemfile:
group :development, :test do
  # ... other gems ...
  gem 'factory_girl'
end
  • Then bundle install to install it!
  • Next make a file at spec/support/factories.rb. This is where you'll be creating your factories!
  • Now open the spec_helper.rb file and add these lines to the top
require 'factory_girl'
require_relative 'support/factories'
  • Create our database. Go to 'config/database.yml' and update the file with the database names. Then rake db:create

  • Lastly, we will just need require spec_helper at the top of spec test files.

##Creating Factories

###Setting up the space

Open up spec/support/factories.rb. Add the following code:

FactoryGirl.define do

  # Your factories will go here!

end

You've now set up the space to write all your factories.

###Write Tests

  • In your 'spec/model' directory create spec files for each model object
  • New migration for active record pokemon object rake db:create_migration NAME=create_pokemons
  • Create table with name, ability, poketype(type can't be a column), strength, age
  • rake db:migrate to create the table in the db

In the 'spec/models/pokemon_spec.rb' we can start with the following tests:

describe "#name" do
  it "should have a name" do
    expect(pokemon.name).to eq("Bulbasaur")
  end
end

describe "#poketype" do
  it "should have poketype" do
    expect(pokemon.poketype).to eq("Grass")
  end
end

describe "#ability" do
  it "should have a name" do
    expect(pokemon.ability).to eq("Overgrow")
  end
end

describe "#evolve" do
  it "should be evolved" do
    expect(pokemon.name).to eq("Ivysaur")
    expect(pokemon.age).to eq(3)
  end
end

Let's use FactoryGirl to create these objects

###Writing a Simple Factory (Documentation)

Let's start with our Pokemon object. A Pokemon object has the following attributes. In our factories.rb file, this is what we would write:

factory :pokemon do
  name "Bulbasaur"
  ability "Overgrow"
  poketype "Grass"
  strength 25
  age 2
end

It's that simple! Now we've set default values for our Test Pokemon's name, ability, strength, poketype and age. Every time we use FactoryGirl to create a pokemon it'll be this Bulbasaur.

###Using a Factory (Documentation)

Now that this factory is set up, ANY time we want to create a Pokemon object in one of our tests files, we can say:

pokemon = FactoryGirl.create(:pokemon)

This will return a new Pokemon object with the name "Bulbasaur" and the age 2, just as if we had called:

bulbasaur = Pokemon.create(name: "Bulbasaur", ability: "Overgrow", poketype: "Grass", strength: 25, age: 2)

###Using Factories with Custom info

What if I love using my factory, but for this one test I want to see if the pokemon evolved so I need an older pokemon to test? I can still use the factory I set up earlier, but override one of the default values with my own info:

Ivysaur = FactoryGirl.create(:pokemon, name: "Ivysaur", age: 3)

Now we've created a pokemon with all the default info we specified in our factory, except for name and age which we've overridden. This can be very useful if you have a lot of info in your factory, and just want to change a small piece of it!

###Different Kinds of Objects (aka Inheritance)

Let's say we're making a lot of tests that use evolved pokemon, as well as a lot of tests that use default young ones. Instead of needing to manually override "age" and name every time, we should create a special factory for evolved pokemon!

We can make a new factory called :evolved_pokemon inside the :pokemon factory like so:

factory :pokemon do
  name "Bulbasaur"
  ability "Overgrow"
  poketype "Grass"
  strength 25
  age 2

  factory :evolved_pokemon do
    name "Ivysaur"
    age 3
  end
end

If we call FactoryGirl.create(:evolved_pokemon), all of the default traits we set up in the normal pokemon factory will still be there, except the ones we explicitly override in the evolved_pokemon factory.

###Objects with Associations (Documentation)

Let's say we now have an Pokemaster model class as well as Pokemon, and there's a one-to-many relationship between them: an Pokemaster has many Pokemon, and a Pokemon belongs to Pokemaster.

  • Let's add a new test in the 'spec/model' directory
  • New migration for active record pokemon object rake db:create_migration NAME=create_pokemasters
  • Create table with name, age, email
  • Create class in the 'app/model' directory
  • rake db:migrate to create the table in the db
  • Add associations - What do we need?
  • Create migration to add column for belongs_to pokemaster
  • create our test in 'spec/models/pokemasters_spec.rb'
describe "#name" do
  it "should have a name" do
    expect(pokemaster.name).to eq("Ash")
  end
end

describe "#age" do
  it "should have a name" do
    expect(pokemaster.age).to eq(14)
  end
end

We don't want to have any wild pokemon being created without having pokemasters, so let's add a factory for Pokemasters to our factories.rb file:

factory :pokemaster do
  name "Ash"
  age 14
  email "pokemaster@gmail.com"
end

Once that factory is set up we can go back to our pokemon factory and just add the word pokemaster as a line. If we don't set value, FactoryGirl will go and look for a factory of that name and create the associated object for us!

factory :pokemon do
  name "Bulbasaur"
  ability "Overgrow"
  poketype "Grass"
  strength 25
  age 2
  pokemaster
end

Now every time we create a new pokemon, it'll create a new pokemaster to go with it. Let's check it out by adding the following tests to our pokemon_spec.rb:

describe '#pokemaster' do
  it "should have pokemaster" do
    expect(pokemon.pokemaster.name).to eq("Ash")
  end

  it "should have Brock as a pokemaster" do
    expect(brocks_pokemon.pokemaster.name).to eq("Brock")
  end
end

Or, let's say we want to create a pokemon with a specific, pre-existing pokemaster. We can just overwrite this default information like before:

brock = FactoryGirl.create(:pokemaster, name: "Brock")
brocks_pokemon = FactoryGirl.create(:pokemon, pokemaster: brock)
brocks_pokemon.pokemaster
=> #<Pokemaster:0x007f8e56d73a70 id: 1, name: "Brock", age: 14, email: "pokemaster@gmail.com">

##Fancier Factories

###Objects with Uniqueness Constraints (aka Sequencing)

As of right now, all pokemasters I create will have the same email (unless otherwise specified). What if I have a uniqueness constraint on pokemasters' emails, so I can't have duplicate emails in my database? Let's add a feature test and we can display our pokemasters and their emails:

feature 'pokemasters directory' do
  scenario "view list of pokemasters" do
    visit '/pokemasters'

    expect(page).to have_content("Pokemasters Directory")
    expect(page).to have_content ("Ash")
    expect(page).to have_content ("pokemaster@gmail.com")
  end
end

We can set up a sequence, meaning that each time you use the factory to create a new pokemaster, the n in the block you wrote will increment by 1. So first we'll have "pokemaster1@gmail.com", then "pokemaster2@gmail.com", etc!

# factories.rb
factory :pokemaster do
  # ..other attributes
  sequence(:email) { |n| "pokemaster#{n}@gmail.com" }
end

These are some examples while using ActiveRecord and a TDD approach to using FactoryGirl in a Sinatra app. These leverage Rspec and model/unit and acceptance testing. FactoryGirl can do a lot of helpful things for testing. Here are some more resources for using FactoryGirl and getting it set up in rails.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment