【问题标题】:Mocking/Stubbing an Application Controller method with Mocha (Using Shoulda, Rails 3)使用 Mocha 模拟/存根应用程序控制器方法(使用 Shoulda,Rails 3)
【发布时间】:2011-04-21 00:24:46
【问题描述】:

在为控制器编写功能测试时,我遇到了这样一个场景,我有一个 before_filter 从数据库中请求我的一个测试需要的一些信息。我正在使用 Factory_girl 生成测试数据,但我想避免在没有明确需要时访问数据库。我还想避免在这里测试我的 before_filter 方法(我计划在单独的测试中测试它)。据我了解,模拟/存根是实现此目的的方法。

我的问题是,在这种情况下模拟/存根此方法的最佳方法是什么。

我的前置过滤器方法根据在 URL 中找到的子域在数据库中查找站点,并设置要在控制器中使用的实例变量:


#application_controller.rb

def load_site_from_subdomain
  @site = Site.first(:conditions => { :subdomain => request.subdomain })
end

我的控制器使用这个方法作为 before_filter:


# pages_controller.rb

before_filter :load_site_from_subdomain

def show
  @page = @site.pages.find_by_id_or_slug(params[:id]).first
  respond_to do |format|
    format.html { render_themed_template }
    format.xml  { render :xml => @page }
  end
end

如您所见,它依赖于要设置的@site 变量(通过 before_filter)。然而,在测试期间,我想让测试假设 @site 已设置,并且它至少有 1 个关联页面(由 @site.pages 找到)。我想稍后再测试我的load_site_from_subdomain 方法。

这是我在测试中的内容(使用 Shoulda 和 Mocha):


context "a GET request to the #show action" do

  setup do
    @page = Factory(:page)
    @site = Factory.build(:site)

    # stub out the @page.site method so it doesn't go 
    # looking in the db for the site record, this is
    # used in this test to add a subdomain to the URL
    # when requesting the page
    @page.stubs(:site).returns(@site)

    # this is where I think I should stub the load_site_from_subdomain
    # method, so the @site variable will still be set
    # in the controller. I'm just not sure how to do that.
    @controller.stubs(:load_site_from_subdomain).returns(@site)

    @request.host = "#{ @page.site.subdomain }.example.com"
    get :show, :id => @page.id
  end

  should assign_to(:site)
  should assign_to(:page)
  should respond_with(:success)

end

这让我的测试结果出现错误,告诉我@site 为零。

我觉得我做错了。我知道只需 Factory.create 站点就很容易,因此它存在于数据库中,但正如我之前所说,我想减少数据库的使用以帮助保持我的测试速度。

【问题讨论】:

    标签: ruby-on-rails mocking shoulda mocha.js stubbing


    【解决方案1】:

    尝试存根 'Site.first',因为它是您需要存根的 @site var 的设置,而不是 before_filter 返回的 var。

    【讨论】:

    • 是的,我同意。通过取消 Site.first 调用来提供@site(在您的测试中)也意味着您正在测试您的显示操作,过滤器也按预期工作。
    • 太棒了。谢谢!我知道这将是一件简单的事情。现在更有意义了。
    • 在控制器中,我使用了一个名为 find_by_id_or_slug 的命名范围。现在看来,范围不想工作(测试返回无方法错误),我将它移动到模型中的类方法并且它工作正常。我对此没意见,但知道为什么吗?
    • 我不认为 Rails 允许使用“或”的动态方法。
    • 不,它不是动态查找器,它是一个命名范围,我在 Page 模型中自己定义了它
    【解决方案2】:

    你的@sitenil 的原因是因为你的load_site_from_subdomain@site 赋值——它不返回任何值,因此你对load_site_from_subdomain 的存根根本没有赋值给@site。有两种解决方法:

    第一种方式:

    更改load_site_from_subdomain 只做一个返回值:

    def load_site_from_subdomain
      Site.first(:conditions => { :subdomain => request.subdomain })
    end
    

    然后删除 before_filter :load_site_from_subdomain 并将您的 show 更改为:

    def show
      @site = load_site_from_subdomain
      @page = @site.pages.find_by_id_or_slug(params[:id]).first
      respond_to do |format|
        format.html { render_themed_template }
        format.xml  { render :xml => @page }
      end
    end
    

    然后在测试中做stubbing:

    @controller.stubs(:load_site_from_subdomain).returns(@site)
    

    确保我们的 @site 通过 load_site_from_subdomain 间接存根

    第二种方式

    要存根Site.first,我不太喜欢功能测试中的这种方法,我们并不真正关心如何检索模型,而是关心respond 的行为。无论如何,如果你想走这条路,你可以在你的测试中把它存起来:

    Site.stubs(:first).returns(@site)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-02-28
      • 2019-04-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多