Skip to content

Instantly share code, notes, and snippets.

@jennli
Last active March 16, 2016 17:48
Show Gist options
  • Save jennli/49e0ad1a1b5590dae116 to your computer and use it in GitHub Desktop.
Save jennli/49e0ad1a1b5590dae116 to your computer and use it in GitHub Desktop.

#Test Driven Development

  • Write test cases before you write the code

###Test frameworks

  • UnitTest vs. RSpec
  • three phases: given , when, then

Gems and installation commands

  gem 'rspec-rails'
  • run bundle install for the gem above and run:
  bin/rails generate rspec:install
  • create database
bin/rake db:create RAILS_ENV=test
  • run generators
bin/rails g
  • in rails_helper.rb
ENV['RAILS_ENV'] ||= 'test'

layer

  • combination of unit test => integration testing

campaign example

model

bin/rails g rspec:model campaign
  • in spec/model/campaign_spec.rb
require 'rails_helper'

RSpec.describe Campaign, type: :model do
  # pending "add some examples to (or delete) #{__FILE__}"
  describe "validations" do
    # it is a method that
    # takes a test example description and
    # a block of code where you can construct your test
    # every test scenario must be put with in an 'it' or 'specify' block
    it "doesn't allow creating a compain with no name" do
      # Given: compain with no title
      c = Campaign.new

      # When: we validate the campaign
      campaign_valid = c.valid?

      #Then: expect that campaign_valid = false
      expect(campaign_valid).to eq(false)

    end
    it "requires a goal" do
      #GIVEN: campaign with no goal
      c = Campaign.new

      # When
      c.valid?

      # Then
      expect(c.errors).to have_key(:goal)
      # we call methods like: have_key matchers
      # RSpec rails comes with many built-in matchers
    end

  end
end
  • in command
bin/rails g model campaign name description:text goal:integer end_date:datetime
bin/rake db:migrate RAILS_ENV=test
  • finally running the test
rspec

controller

  • command line
bin/rails g controller campaigns
  • create action and template files for new action
  • routes
  resources :campaigns, only: [:new]
  • shell
rspec spec/controllers/campaigns_controller_spec.rb:6
  • rails/controller/campaigns_controller
require 'rails_helper'

RSpec.describe CampaignsController, type: :controller do
  describe "#new" do
    it "renders the new template" do
      get :new
      # response is an object that is given to us by RSpec that will help test
      # things like redirect / render
      # render_template is a an RSpec (rspec-rails) matcher that help us check
      # if the controller renders the template with the given name.
      expect(response).to render_template(:new)
    end
  end
end
  • another campaign controller test. in controller
  def new
    @campaign = Campaign.new
  end
  • in campaign_controller_spec.rb
it "instantiates a new Campaign object and sets it to @campaign" do
      get :new
      # be_a_new is called a matcher
      expect(assigns(:campaign)).to be_a_new(Campaign)
    end
  • and finally
rspec spec/controllers/campaigns_controller_spec.rb:14
  • another: create. controller:
  def create

    render nothing: true
  end
  • controller_spec
  describe "#create" do

    context "with valid attributes" do
      it "creates a record in the database" do
        campaign_count_before = Campaign.count
        post :create, campaign: {name: "some valid name",
          description: "some valid description",
          goal: 1000000
        }
        campaign_count_after = Campaign.count
        expect(campaign_count_after - campaign_count_before).to eq(1)
      end
      
      it "redirects to the campaign show page"
      it "sets a flash notice message"
    end
    context "with invalid attributes" do
      it "doesn't create a record in the database"
      it "renders the new template"
      it "sets a flash alert message"
    end
  end

Factory

  • gem 'factory_girl_rails'
  • in shell, run
bin/rails g factory_girl:model campaign
  • make sure in rails_helper.rb, the following is present in factory girl config
config.include FactoryGirl::Syntax::Methods
  • this will generate the spec/factories/campaigns.rb file. we can give it some valid attributes:
# key thing to remember is that factories should always provide a set of valid attributes
# which means the factoires should always create record with no validation errors
FactoryGirl.define do
  factory :campaign do
    # put values you want to assign as a variable,
    # then put values you want the factory to assign to that attr
    # if yu give value without a block, it's going to be the same for all
    # with block, faker will try to generate different values everytime
    # to be 100% sure that the values are always unique, use 'sequence'
    sequence(:name)        {|n| "#{Faker::Company.bs}-#{n}"}
    sequence(:description) {|n| "#{Faker::Lorem.paragraph}-#{n}"}
    goal        100000
    end_date    60.days.from_now
  end
end
  • then go to bin/rails c, use factory to generate file
bin/rails c
  > FactoryGirl.create(:campaign)
   (0.2ms)  BEGIN
  Campaign Exists (0.3ms)  SELECT  1 AS one FROM "campaigns" WHERE "campaigns"."name" = 'harness world-class content' LIMIT 1
  SQL (0.4ms)  INSERT INTO "campaigns" ("name", "description", "goal", "end_date", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5, $6) RETURNING "id"  [["name", "harness world-class content"], ["description", "Et facilis nam officiis. Earum provident tempore quia aut dolore assumenda aspernatur. Voluptatem voluptatem quaerat atque aut ab."], ["goal", 100000], ["end_date", "2016-04-05 18:21:53.063636"], ["created_at", "2016-02-05 18:22:00.020702"], ["updated_at", "2016-02-05 18:22:00.020702"]]
   (1.3ms)  COMMIT
+----+------------+------------+--------+------------+------------+------------+
| id | name       | descrip... | goal   | end_date   | created_at | updated_at |
+----+------------+------------+--------+------------+------------+------------+
| 3  | extend... | Et faci... | 100000 | 2016-04... | 2016-02... | 2016-02... |
+----+------------+------------+--------+------------+------------+------------+
1 row in set
2.2.3 :004 > _.name
"extend scalable markets-1"
  • use attributes_for from Factory to view attribute values
FactoryGirl.attributes_for(:campaign)
{
           :name => "incentivize transparent synergies-1",
    :description => "Mollitia illum ab. Quia sit reprehenderit quaerat sit placeat modi nisi. Facere praesentium rerum explicabo ut laboriosam. Non consequatur pariatur. Ab eos quidem earum consequuntur.-1",
           :goal => 100000,
       :end_date => Tue, 05 Apr 2016 18:33:18 UTC +00:00
}
  • let
let (:campaign) {FactoryGirl.create(:campaign)}
# def campaign
# @campaign ||= FactoryGirl.create(:campaign)
#

update

  • testing update is a bit tricky because you need to run reload on the record after you update/PATCH it
  it "updates the record with new parameters" do
      # Given: use the campaign method we defined with let
        patch :update, id: campaign.id, campaign: {name: "new valid name"}
        # we must use campaign.reload in this scenario because the controller will
        # instantiate another campaign object based on the id but it will
        # live in another memory location. which means "campaign' here will still 
        # have the old data not the updated one. reload will make active record rerun
        # the qury and feches the information from the DB again
        expect(campaign.reload.name).to eq("new valid name")
    end

destroy

  describe "#destroy" do
      # let!(:campaign) { FactoryGirl.create(:campaign) }
      it "removes the campaign from the database" do
        # campaign
        # expect { delete :destroy, id: campaign.id }.to change { Campaign.count }.by(-1)
        campaign
        count_before = Campaign.count
        delete :destroy, id:campaign.id
        expect(count_before - Campaign.count).to eq(1)
      end

      it "redirects to the campaign index page" do
        delete :destroy, id:campaign.id
        expect(response).to redirect_to(campaigns_path)
      end

      it "displays a notice" do
        delete :destroy, id:campaign.id
        expect(flash[:notice]).to be
      end
    end
  • Drop and recreate test db
rake db:test:prepare
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment