[英]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
. 之所以你
@site
是nil
,因为你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.