Rspec: identical tests fails when repeated

I’m doing RSpec controller testing with CanCan authorization, and I’m
seeing something I’ve never seen in RSpec before: the same test run
twice fails on the second one. I am NOT doing before(:all) or other
things that should cause state to persist between tests:

Here’s the relevant code:

context “POST create” do

context "with user logged in" do
  before(:each) do
    @user = mock_model(User, :admin? => false, :guest? => false)
    controller.stub(:current_user) { @user } # aka login
    @attributes = valid_attributes
  end

  # succeeds
  it "should create a new Premise" do
    lambda{
      post(:create, {:premise => @attributes}, :format => :json)
    }.should change(Premise, :count).by(1)
  end

  # fails with CanCan::AccessDenied
  it "should create a new Premise again" do
    lambda{
      post(:create, {:premise => @attributes}, :format => :json)
    }.should change(Premise, :count).by(1)
  end

end

I can’t figure out why the first test passes and the second test fails
– this suggests that there’s some state that’s being preserved across
the tests, but I can’t imagine what that might be, nor how to check for
it.

Any suggestions? (Of course, I can provide full model and Ability class
info if it would be helpful.) TIA.

UPDATE: I noticed that I had a “rails console --sandbox” running in
another window. When I quit that, the behavior of the test changed.
Dramatically. I haven’t sorted through the new errors, but it’s clear
that the --sandbox process was causing the db to respond differently.

On 6 April 2012 01:10, Fearless F. [email protected] wrote:

@attributes = valid_attributes
it “should create a new Premise again” do
NOW what happens:

  • the first test fails with response.message = “Not Acceptable”
  • the second test fails because count is not incremented.

First look in test.log to see if there is any difference there, if
still no joy then debug the create action to see what is going on.
Look at the Rails Guide on Debugging if you don’t know how to do this.

Colin

Colin L. wrote in post #1055228:

On 6 April 2012 01:10, Fearless F. [email protected] wrote:

  • the first test fails with response.message = “Not Acceptable”
  • the second test fails because count is not incremented.

First look in test.log to see if there is any difference there, if
still no joy then debug the create action to see what is going on.
Look at the Rails Guide on Debugging if you don’t know how to do this.

Colin

Colin, thanks. To be clear, it appears that the #create method is
actually working. For the moment, I’m going to assume the first test is
okay and that the “Not Acceptable” response is an issue to chase down
later.

I see what’s going on, but not how to fix the test: In the first test,
current_user.id = 1001. Ability#new gets called and sets up the
appropriate abilities. On the second test, current_user.id = 1002, but
Ability#new doesn’t get called. (Is it cached?) When it tries to
create a premise object, CanCan’s load_and_authorize_resource method
creates a Premise with user_id = 1001, which subsequently fails because
current_user’s id = 1002.

So the short question: Why isn’t Ability#new getting called in every
test? Or what must I do to set up the abilities?

You can stop reading here if you understand the question. :slight_smile: Gory
details follow:

My Abilities class has (effectively):

file: app/models/ability.rb

class Ability
def initialize(user)
can :manage, Premise, :user_id => user.id
end
end

meaning that access to a Premise is limited to its owner. My
PremisesController has:

file: app/controllers/premises_controller.rb

class PremisesController < ApplicationController
load_and_authorize_resource
respond_to :json

def create
  @premise.save!
  respond_with @premise
end

end

In the first test:

current_user.id = 1001
Ability#new gets called with current_user
arguments to Premise#initialize = {:user_id => 1001, …}

In the second test:

current_user.id = 1002
Ability#new does NOT get called
arguments to Premise#initialize = {:user_id => 1001, …}

… which leads to a CanCan::AccessDenied error, since premise.user_id
!= current_user.id

REDACTED!

Okay, it’s 100% my error.

Ability#initialize IS getting called.

The source of my woes: I was explicitly passing :user_id => 1001 to
Premise#initialize, which was overriding the :user_id that CanCan was
providing. It was bad luck (or bad judgement) that I chose 1001 as the
id – this caused it to work on the first test and fail on the second.

The halos of Saint Bates and Saint Chelimsky are still untarnished!

(Now I can go debug why respond_with @premise is getting a “Not
Acceptable” response.)

MORE UPDATE: I beefed up the tests to look at the return code, the # of
Premises generated and the assignment. And things only got stranger:

context “POST create” do

context "with user logged in" do
  before(:each) do
    @user = mock_model(User, :admin? => false, :guest? => false)
    controller.stub(:current_user) { @user } # aka login
    @attributes = valid_attributes
  end

  it "should create a new Premise" do
    count = Premise.count
    post(:create, {:premise => @attributes}, :format => :json)
    Premise.count.should == count + 1
    response.message.should == "OK"
    assigns(:premise).should be_instance_of(Premise)
  end

  it "should create a new Premise again" do
    count = Premise.count
    post(:create, {:premise => @attributes}, :format => :json)
    Premise.count.should == count + 1
    response.message.should == "OK"
    assigns(:premise).should be_instance_of(Premise)
  end

end

end

NOW what happens:

  • the first test fails with response.message = “Not Acceptable”
  • the second test fails because count is not incremented.

Why should there be any difference between the two?