开发者

Why has shoulda controller test assign_to no access to instance variable?

For my Rails 3 application I use FactoryGirl together with shoulda context (1.0.0.beta1) and matchers (1.0.0.beta3) for my functional tests. My problem: in the code example below, the assign_to test fails because @user - to my surprise - turns out to be nil. In the outer setup block, @user is开发者_如何学JAVA assigned a valid model instance, but from within the should assign_to statement the instance variable is not accessible. Why is that and what is the correct way to write that test?

class UsersControllerTest < ActionController::TestCase
  context "as admin" do
    setup do
      @user = Factory.create(:user)
    end

    context "getting index" do
      setup do
        get :index
      end

      should assign_to(:users).with([@user])
    end
end


I discovered that passing the value as a black miraculously works. However, after digging into the actual AssignToMatcher code, it doesn't seem to make sense why the parameter method wouldn't work while the block method would.

For the sake of the +250 rep I'm investing in this, I'd still like an answer that explains why the param method isn't working (and how to fix it), but until then, at least I have a workaround.

@dblp1, hopefully this works for you too. Here's an example specific to your code:

class UsersControllerTest < ActionController::TestCase
  context "as admin" do
    setup do
      @user = Factory.create(:user)
    end

    context "getting index" do
      setup do
        get :index
      end

      should assign_to(:users).with { [@user] }
    end
  end
end


(I am pasting as an answer as it is fairly long)

I mocked a bit your test and checked what was the results passed.

Interestingly enough, when I call the matcher @post=null (as I believe in your case). The issues I think (after a while of investigation), it coming from the the order the setup do block is called and the fact that the variables defined in one context are not visible in the nested context.

modified class

context "as admin" do
 setup do
   @post = Post.new
   puts "Step 1 - inside setup do"
   puts @post.class
 end
 puts "Step 2 - outside setup do 1"
 puts @post.class

 context "getting index" do
   setup do
     get :index
   end
   puts "Step 3 - calling shoulda"
   puts @post.class
   should assign_to(:posts).with([@post])
   #should assign_to(:posts).with  { [@post] }
   end
end

And the results in the console

ruby -I test test/functional/posts_controller_test.rb
Step 2 - outside setup do 1
NilClass
Step 3 - calling shoulda
NilClass
Loaded suite test/functional/posts_controller_test
Started
Step 1 - inside setup do
Post

So the setup cycle is called at the end (and not at the beginning) and then your is Nil when passed to the matcher.

Even if I remove the first setup do does not work pretty well.

Step 1 - inside setup do
Post
Step 2 - outside setup do 1
Post
Step 3 - calling shoulda
NilClass

Finally, putting the post in the inner context

Step 3 - calling shoulda
Post

If you call @user = Factory.create(:user) directly inside the "getting index" context, I believe it will work.


When you are working with indexes you should use the plural of the instance variable.

@users rather than @user

You should also populate it as an array.

Finally, Shoulda matchers should start with "it" and be contained in braces, at least in the RSpec world, which is what I use. Not sure if this is the case with Test::Unit or whether your formatting above will work.

Try something like this.

class UsersControllerTest < ActionController::TestCase
  context "as admin" do
    setup do
      @user = Factory.create(:user)
    end

    context "getting index" do
      setup do
        @users = Array.new(3) { Factory(:user) }
        get :index
      end

      it { should assign_to(:users).with(@users) }
    end
end
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜