使用 Mocha 模拟/存根应用程序控制器方法(使用 Shoulda、Rails 3)

发布于 2024-09-26 05:43:31 字数 1987 浏览 9 评论 0原文

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

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

我的 before 过滤器方法根据 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 站点即可很容易,因此它存在于数据库中,但正如我之前所说,我想减少数据库使用量以帮助保持测试速度。

While writing functional tests for a controller, I came across a scenario where I have a before_filter requesting some information from the database that one of my tests requires. I'm using Factory_girl to generate test data but I want to avoid hitting the database when its not explicitly needed. I'd also like to avoid testing my before_filter method here (I plan to test it in a separate test). As I understand, mocking/stubbing is the way to accomplish this.

My question is, what is the best way to mock/stub this method in this scenario.

My before filter method looks for a site in the db based on a subdomain found in the URL and sets an instance variable to be used in the controller:


#application_controller.rb

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

My controller that uses this method as a 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

As you can see, it relies on the @site variable to be set (by the before_filter). During testing however, I'd like to have the test assume that @site has been set, and that it has at least 1 associated page (found by @site.pages). I'd like to then test my load_site_from_subdomain method later.

Here is what I have in my test (using 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

This leaves me with an error in my test results telling me that @site is nil.

I feel like I'm going about this the wrong way. I know it would be easy to simply just Factory.create the site so it exists in the db, but as I said earlier, I'd like to reduce the db usage to help keep my tests speedy.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

千里故人稀 2024-10-03 05:43:31

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

Try stubbing out 'Site.first' since it the the setting of the @site var that you need to stub and not the returned var from the before_filter.

盛夏尉蓝 2024-10-03 05:43:31

您的 @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

然后在测试中进行存根:

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

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

第二种方式

为了存根 Site.first,我不太喜欢这种方法,因为在功能测试中,我们并不真正关心如何检索模型,而是 响应的行为。不管怎样,如果你想走这条路,你可以在测试中把它去掉:

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

The reason why your @site is nil because your load_site_from_subdomain does the value assignment for @site -- it does not return any value hence your stubbing for load_site_from_subdomain simply doesn't assign the value to @site. There are two work-arounds for this:

First way:

Change load_site_from_subdomain to just do a return value:

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

and then remove the before_filter :load_site_from_subdomain and change your show to:

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

And then do the stubbing in the test:

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

that ensure our @site is stubbed indirectly via load_site_from_subdomain

Second way

To stub the Site.first, I do not really like this approach as in functional test, we do not really care about how the model is retrieved but the behaviour of the respond. Anyway, if you feel like going this path, you could stub it out in your test:

Site.stubs(:first).returns(@site)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文