Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

rspec testing association

I want to test that a staff member is associated with a company in my rspec controller tests.

I would like to end up with this in my create action of the staff controller:

staff.companies << current_company

Where current_company is collected from a session variable.

How do I write a test for this?

I've got these models

class Company < ActiveRecord::Base
  has_many :employees
  has_many :staff, :through => :employees
end

class Employee < ActiveRecord::Base
  belongs_to :company
  belongs_to :staff
end

class Staff < ActiveRecord::Base
  has_many :employees
  has_many :companies, :through => :employees
end

The following test is my attempt to spec the assocation and it fails when I enter in the association code:

    it "should belong to the current_company" do
      staff.should_receive(:companies)
      post :create
    end

If I enter the 'staff.companies << current_company' code in my controller I get this error when running that test:

 Failure/Error: post :create
 NoMethodError:
   You have a nil object when you didn't expect it!
   You might have expected an instance of Array.
   The error occurred while evaluating nil.<<

Staff controller create method:

  def create
    @staff = Staff.new(params[:staff])

    if @staff.save
      @staff.companies << current_company
      redirect_to staff_index_path, :notice => "Staff created successfully!"
    else
      @company = @staff.firm || current_company
      flash[:alert] = "Staff failed to create"
      render "new"
    end
  end
like image 322
map7 Avatar asked Mar 24 '11 01:03

map7


People also ask

Is RSpec TDD or BDD?

RSpec is a Behavior-Driven Development tool for Ruby programmers. BDD is an approach to software development that combines Test-Driven Development, Domain Driven Design and Acceptance Test-Driven Planning. RSpec helps you do the TDD part of that equation, focusing on the documentation and design aspects of TDD.

Is RSpec used for unit testing?

RSpec is a unit test framework for the Ruby programming language. RSpec is different than traditional xUnit frameworks like JUnit because RSpec is a Behavior driven development tool. What this means is that, tests written in RSpec focus on the "behavior" of an application being tested.

How do I run an RSpec test?

To run a single Rspec test file, you can do: rspec spec/models/your_spec. rb to run the tests in the your_spec. rb file.

How do I use RSpec gems?

Boot up your terminal and punch in gem install rspec to install RSpec. Once that's done, you can verify your version of RSpec with rspec --version , which will output the current version of each of the packaged gems. Take a minute also to hit rspec --help and look through the various options available. That's it.


2 Answers

I would use a different approach, since testing that the model should receive a certain message couples your tests too tightly to the implementation. Do you really care whether companies receives #<< or some other method?

Really, what you want to test is whether the user's company is recorded when they post to the page. It doesn't matter how it was recorded. So I'd do something like this:

it "should add the company to the user's list of companies" do
  lambda do 
    post :create
  end.should change(staff.companies, :count).from(0).to(1)
  staff.companies.map(&:name).should include("Acme, Inc.")
end

This is testing behavior instead of implementation. The advantage is that your test wont fail when someone changes that << to the equivalent push. It also has the advantage of being clearer about your intention and therefore better documenting the code.

like image 134
Ian Avatar answered Dec 04 '22 16:12

Ian


If you're in your controller spec, I would use stub_chain

staff.stub_chain(:company, :<<).once.and_return(true)

which will mock out the company call AND the << call AND expect it to be called once.

(At least, that .once should work with stub_chain...)

like image 40
RyanWilcox Avatar answered Dec 04 '22 17:12

RyanWilcox