简体   繁体   English

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

[英]Mocking/Stubbing an Application Controller method with Mocha (Using Shoulda, Rails 3)

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. 在为控制器编写功能测试时,我遇到了一个场景,我有一个before_filter从数据库中请求我的一个测试需要的一些信息。 I'm using Factory_girl to generate test data but I want to avoid hitting the database when its not explicitly needed. 我正在使用Factory_girl来生成测试数据,但我想避免在未明确需要时访问数据库。 I'd also like to avoid testing my before_filter method here (I plan to test it in a separate test). 我还想避免在这里测试我的before_filter方法(我打算在单独的测试中测试它)。 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: 我的before filter方法根据URL中找到的子域查找db中的站点,并设置要在控制器中使用的实例变量:


#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: 我的控制器使用此方法作为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). 如您所见,它依赖于@site变量(由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 ). 然而,在测试期间,我想让测试假设已经设置了@site ,并且它至少有一个关联页面(由@site.pages找到)。 I'd like to then test my load_site_from_subdomain method later. 我想稍后测试我的load_site_from_subdomain方法。

Here is what I have in my test (using Shoulda & Mocha): 这是我在测试中使用的(使用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. 这让我在测试结果中出错,告诉我@site是零。

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. 我知道简单地只是Factory.create网站以便它存在于数据库中是很容易的,但正如我之前所说的,我想减少数据库的使用以帮助我保持测试的速度。

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

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 . 之所以你@sitenil ,因为你load_site_from_subdomain做为赋值@site -它不返回任何值,因此您的磕碰load_site_from_subdomain根本不分配值@site There are two work-arounds for this: 有两种解决方法:

First way: 第一种方式:

Change load_site_from_subdomain to just do a return value: load_site_from_subdomain更改为只返回值:

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: 然后删除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

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 确保我们的@site通过load_site_from_subdomain间接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 . 为了存根Site.first ,我不喜欢这种方法,因为在功能测试中,我们并不真正关心如何检索模型,而是关注respond的行为。 Anyway, if you feel like going this path, you could stub it out in your test: 无论如何,如果您想要走这条路,您可以在测试中将其存根:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM