我正在通过一个rspec教程(peepcode教程)。我生成了一些脚手架,我希望有人可以帮助解释如何重新编写描述以便为新手阅读更清楚。
describe "POST create" do
describe "with valid params" do
it "assigns a newly created weather as @weather" do
Weather.stub(:new).with({'these' => 'params'}) { mock_weather(:save => true) }
post :create, :weather => {'these' => 'params'}
assigns(:weather).should be(mock_weather)
end
end
这行代码是我试图理解的这一行
Weather.stub(:new).with({'these' => 'params'}) { mock_weather(:save => true) }
我从未见过将方法放在括号内。这究竟意味着什么?
{ mock_weather(:save => true) }
答案 0 :(得分:6)
该陈述意味着:
使用参数new
将Weather
方法保存在类'these'=>'params'
上,并返回表达式mock_weather(:save => true)
的值
写一个类似的,也许更清晰的方法是:
Weather.stub(:new).with({'these'=>'params'}).and_return(mock_weather(:save => true))
语法{
< some code
> }
创建code block,在调用存根时执行。
两种表单.and_return()
和{}
的返回值略有不同;在第一种情况下,确定何时定义存根,在第二种情况下确定何时接收到消息。它们通常可以互换 - but sometimes not。
修改强>
我觉得this answer误导了嘲笑并值得回应:
关于你的第一个问题 为了让它更清晰一点,“我们可以 从不使用模拟开始。一个模拟 当你不能时,对象很有用 取决于a的可预测行为 辅助对象,是一个对象 不重要但必须存在于 你的测试用例。典型的例子 模拟对象的用法是数据库 查询,网络使用,文件i / o。您 不希望你的测试失败,因为 你的电脑失去网络连接, 或者数据库不可用。
没错,模拟可以消除对外部资源的依赖,但这不是他们唯一的目的。模拟的真正价值在于它们允许您为尚不存在的代码编写测试。例如,在Rails中,您可能决定首先编写视图规范。
describe "posts/show.html.erb" do
it "displays the author name" do
assign(:post,mock('post',:author=>"Mark Twain"))
render
rendered.should contain("written by Mark Twain")
end
end
此规范不需要存在数据库,控制器或模型。它所做的只是声明视图需要呈现一个字符串并验证它是否被渲染 - 这就是Rails视图应该关注的全部内容。唯一的依赖是模板文件和实例变量@post
的存在,它由assign
语句处理。它甚至不关心@post
是什么,只是它响应:author
。
您生成的代码 导轨g脚手架不是最佳的。 生成的代码是在a中生成的 将使所有测试通过的方式,和 因为它使用模拟对象。我不 我知道为什么他们这样做了 更好的默认是失败的测试 所以你真的需要做 让它们通过的东西。
脚手架的整个想法是通过生成适用于通常用例的代码来节省时间。您不希望生成的测试实际上也能正常工作吗?
当然,通过使用脚手架,您正在围绕BDD / TDD“测试优先”范例进行最终运行,但可能您已经接受了所涉及的权衡,或者您首先不会使用脚手架。 / p>
对于“为什么使用模拟对象”,它们允许控制器规范与模型和数据库分离。所以,一旦你知道了推理,它就是“最优的”。
使用自动生成的模拟文件 根本不需要做任何事情 测试将继续通过 永远。这是一个坏主意,而且很糟糕 实践。
只要您不破坏主题代码,它们就会通过。因此,他们在回归测试中具有价值,以确保您不会以导致代码不再符合规范的方式引入新代码或重构。
因为必须要写 模型文件中的验证规则, 而你没有使用模拟对象, 你可以肯定的是一个真实的 验证正在进行中。
这种耦合在Rails控制器规范中实际上是不合需要的。控制器应该尽可能少地了解模型,因此控制器规范只需要定义验证通过(或失败)时会发生什么 - 而脚手架提供的模拟就是这样做的。如果您需要测试模型实例是否对给定的参数集有效,请在模型规范中执行此操作。
答案 1 :(得分:1)
关于“如何让它更清晰”的第一个问题,我们可以从不使用模拟开始。
当您不能依赖辅助对象的可预测行为时,模拟对象很有用,辅助对象不是很重要但必须存在于您的测试用例中。模拟对象的典型使用示例是数据库查询,网络使用,文件i / o。您不希望测试失败,因为您的计算机丢失了网络连接,或者数据库不可用。
您从rails g scaffold
生成的代码不是最佳的。生成的代码以一种使所有测试都通过的方式生成,为此它使用模拟对象。我不知道为什么他们这样做,我认为更好的默认是失败的测试,所以你实际上需要做一些事情让它们通过。
我会删除生成的模拟并执行以下操作:
#spec/controllers/weather_controller_spec.rb
describe "POST create" do
describe "with valid params" do
it "assigns a newly created weather as @weather" do
post :create, :weather => {'location' => 'ORD', 'temp'=>'35', 'sample_time'=>'2011-02-04T20:00-0500'}
assigns(:weather).should be_valid
end
it "should redirect you to the weather show page" do
post :create, :weather => {'location' => 'ORD', 'temp'=>'35', 'sample_time'=>'2011-02-04T20:00-0500'}
response.should redirect_to(weather_path(assigns[:weather]))
end
end
describe "without valid params" do
it "should notify that a location is required" do
post :create, :weather => {'temp'=>'35', 'sample_time'=>'2011-02-04T20:00-0500'}
flash[:notice].should == 'Location is required.'
assigns(:weather).should_not be_valid
end
it "should notify that a temperature is required" do
post :create, :weather => {'location' => 'ORD', 'sample_time'=>'2011-02-04T20:00-0500'}
flash[:notice].should == 'A temperature is required.'
assigns(:weather).should_not be_valid
end
it "should notify that a sample time is required" do
post :create, :weather => {'location' => 'ORD', 'temp'=>'35'}
flash[:notice].should == 'A sample time is required.'
assigns(:weather).should_not be_valid
end
end
end
请注意,我们没有使用模拟对象,因此代码减少为使用一些参数进行POST调用并验证该对象是否有效。由于必须在模型文件中编写验证规则,并且您没有使用模拟对象,因此可以确保进行实际验证。
使用自动生成的模拟文件,您根本不需要做任何事情,测试将继续永远通过。这是一个坏主意,也是一种不好的做法。
另请注意,您应编写更多测试来执行参数无效或缺失的情况。再次,通过assigns(:weather).should_not be_valid
,您正在验证您的验证正在完成其工作。
每次调用post :create
时编写参数字典都是重复,脆弱和丑陋的。你应该学习如何使用灯具。例如,使用 Factory Girl
#spec/factories.rb
Factory.define :weather_valid do |f|
f.location "ORD"
f.temp "35"
f.sample_time "2011-02-04T20:00-0500"
end
#spec/controllers/weather_controller_spec.rb
describe "POST create" do
describe "with valid params" do
it "assigns a newly created weather as @weather" do
post :create, :weather => Factory.build(:weather_valid).attributes
assigns(:weather).should be_valid
end
it "should redirect you to the weather show page" do
post :create, :weather => Factory.build(:weather_valid).attributes
response.should redirect_to(weather_path(assigns[:weather]))
end
end
...
这为您提供了可重用且更易读的代码。