【问题标题】:Why has shoulda controller test assign_to no access to instance variable?为什么控制器测试 assign_to 不能访问实例变量?
【发布时间】:2011-09-11 20:19:08
【问题描述】:

对于我的 Rails 3 应用程序,我将 FactoryGirl 与 shoulda 上下文 (1.0.0.beta1) 和匹配器 (1.0.0.beta3) 一起用于我的功能测试。 我的问题:在下面的代码示例中,assign_to 测试失败,因为 @user - 令我惊讶的是 - 结果是 nil。在外部设置块中,@user 被分配了一个有效的模型实例,但在 should assign_to 语句中无法访问实例变量。为什么会这样?编写该测试的正确方法是什么?

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

【问题讨论】:

  • 我遇到了同样的问题,似乎是一个错误。它只发生在从 3.0.0.beta2 更新到最新的 git HEAD 之后。但是,我需要 master 的最新版本,因为一年多没有发布了。

标签: ruby-on-rails-3 functional-testing shoulda


【解决方案1】:

我发现将值作为黑色传递可以奇迹般地起作用。但是,在深入了解实际的AssignToMatcher code 之后,为什么参数方法不起作用而块方法不起作用似乎没有意义。

为了 +250 代表我正在对此进行投资,我仍然想要一个解释为什么 param 方法不起作用(以及如何修复它)的答案,但在那之前,至少我有一个解决方法。

@dblp1,希望这对你也有用。这是特定于您的代码的示例:

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

【讨论】:

  • 我将首先删除方括号should assign_to(:users).with(@user)。似乎在这个确切的主题上有一个错误 (github.com/thoughtbot/shoulda/issues/157)。如果它仍然不起作用,这是一个错误,你应该重新打开这个问题。我很清楚为什么使用积木可以工作。 :-) 我希望它有所帮助。
  • 很好地解决了这个问题。这似乎解释了为什么这种方法在 param 方法上有效,但考虑到我已经获得了最新版本,我不知道为什么它对我来说仍然是一个问题。
  • 应该已修复:-)。确切的错误信息是什么?
  • 当我使用 param 方法而不是 block 方法时,出现如下错误:Expected action to assign nil for user, but got #&lt;User id: 17, email: "test23@example.com" ...&gt;
  • 值得注意的是,我使用的是几天前发布的最新版本的 shoulda (3.0.0)。在此之前我使用的是最新的 github 版本,所以我不应该仍然遇到您链接到的问题 #157,除非错误以某种方式使其重新回到代码中。不过自从AssignToMatcher hasn't been updated in 11 months 之后,我看不出这是怎么可能的。
【解决方案2】:

(我粘贴为答案,因为它相当长)

我稍微嘲笑了一下你的测试并检查了通过的结果。

有趣的是,当我调用匹配器 @post=null 时(我相信你的情况)。我认为(经过一段时间的调查)的问题来自于调用setup do 块的顺序以及在一个上下文中定义的变量在嵌套上下文中不可见的事实。

修改类

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

以及控制台中的结果

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

所以设置循环在最后(而不是在开始)被调用,然后你的当传递给匹配器时是 Nil。

即使我删除第一个 setup do 也不能很好地工作。

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

最后,将帖子置于内部上下文中

Step 3 - calling shoulda
Post

如果您直接在“获取索引”上下文中调用@user = Factory.create(:user),我相信它会起作用。

【讨论】:

  • 我知道这让它看起来像是在工作不正常,但事实并非如此。它需要更深入地了解blocks 的工作原理。仅仅因为任何给定的块是在您的puts 调用之一之前定义的,并不意味着它将在上述puts 调用之前被调用/评估。它只是意味着首先定义了块,但它的调用顺序可以在任何时候出现。不过谢谢!
【解决方案3】:

当您使用索引时,您应该使用实例变量的复数形式。

@users 而不是@user

您还应该将其填充为数组。

最后,Shouda 匹配器应该以“it”开头并包含在大括号中,至少在我使用的 RSpec 世界中是这样。不确定 Test::Unit 是否是这种情况,或者您上面的格式是否有效。

试试这样的。

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

【讨论】:

  • 您的建议是句法偏好,不会改变结果。不幸的是,这是一个实际问题,与变量的命名无关。此外,it 方法是 Rspec 的一部分,而不是 Test::Unit。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-14
  • 2012-12-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-05
相关资源
最近更新 更多