RSpec 脚手架控制器,了解给出的默认值
我正在学习 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) }
I'm working through an rspec tutorial (peepcode tutorial). I generated some scaffold and I was hoping someone can help explain how the describe could be re-written to read a bit clearer for newbie.
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
This line of code is what I'm trying to understand is this one
Weather.stub(:new).with({'these' => 'params'}) { mock_weather(:save => true) }
I have never seen a method being put inside braces. What does this actually mean?
{ mock_weather(:save => true) }
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
该语句的意思是:
使用参数
'se'=>'params'
存根类Weather
上的new
方法,并返回以下值表达式mock_weather(:save => true)
类似且可能更清晰的编写方式是:
Weather.stub(:new).with({'se'=>; 'params'}).and_return(mock_weather(:save => true))
语法
{
<some code
>}< /code> 创建一个代码块,当称为存根。
.and_return()
和{}
两种形式的返回值略有不同;在第一种情况下,确定何时定义存根;在第二种情况下,确定何时接收消息。它们通常可以互换 - 但有时不能。编辑
我觉得这个答案 对模拟具有误导性,值得回应:
确实,模拟可以消除对外部资源的依赖,但这并不是它们的唯一目的。模拟的真正价值在于它们允许您为尚不存在的代码编写测试。例如,在 Rails 中,您可能决定首先编写视图规范。
该规范不要求存在数据库、控制器或模型。它所做的只是断言视图需要渲染一个字符串并验证它是否被渲染——这就是 Rails 视图应该关心的所有事情。唯一的依赖项是模板文件和实例变量
@post
的存在,该变量由assign
语句处理。它甚至不关心@post
是什么,只关心它响应:author
。脚手架的整个想法是通过生成适用于通常用例的代码来节省时间。您难道不希望生成的测试也能实际工作吗?
当然,通过使用脚手架,您将围绕 BDD/TDD“测试优先”范例进行最终运行,但您可能已经接受了所涉及的权衡,否则您一开始就不会使用脚手架。
至于“为什么使用模拟对象”,它们允许控制器规范与模型和数据库解耦。所以,是的,一旦您知道推理,它就是“最佳”。
只要您不破坏主题代码,它们就会通过。因此,它们在回归测试中具有价值,可以确保您不会引入新代码或进行重构,从而导致代码不再符合规范。
这种耦合在 Rails 控制器规范中实际上是不受欢迎的。控制器应该尽可能少地了解模型,因此控制器规范只需要定义验证通过(或失败)时会发生什么——而脚手架提供的模拟正是这样做的。如果您需要测试模型实例对于给定的一组参数是否有效,请在模型规范中执行此操作。
That statement means:
Stub the
new
method on the classWeather
with the parameters'these'=>'params'
and return the value of the expressionmock_weather(:save => true)
A similar and perhaps clearer way to write this would be:
Weather.stub(:new).with({'these'=>'params'}).and_return(mock_weather(:save => true))
The syntax
{
<some code
>}
creates a code block which is executed when the stub is called.The return values of the two forms
.and_return()
and{}
are slightly different; in the first case it is determined when the stub is defined, in the second case it is determined when the message is received. They are generally interchangeable -- but sometimes not.EDIT
I feel this answer is misleading about mocks and deserves a response:
True, mocks can eliminate dependencies on external resources, but that is not their only purpose. The real value of mocks is that they allow you to write tests for code that doesn’t exist yet. In Rails for example, you might decide to start by writing the view specs first.
This spec doesn’t require the existence of a database, a controller, or a model. All it does is assert that the view needs to render a string and verify that it gets rendered --which is all that a Rails view is supposed to be concerned with. The only dependencies are the existence of the template file and the instance variable
@post
, which is handled by theassign
statement. It doesn't even care what@post
is, only that it responds to:author
.The whole idea of a scaffold is to save time by generating code that works for the usual use cases. Wouldn’t you want the generated tests to actually work too?
Of course by using scaffolding you are doing an end run around the BDD/TDD “test first” paradigm, but presumably you’ve accepted the tradeoffs involved, or you wouldn’t be using the scaffolds in the first place.
As to "why use mock objects", they allow the controller spec to be decoupled from the model and the database. So yes it is "optimal" once you know the reasoning.
They will only pass as long as you don’t break the subject code. So they have value in regression testing to ensure that you are not introducing new code or refactoring in a way that causes the code to no longer meet the specs.
This kind of coupling is actually undesirable in a Rails controller spec. The controller should know as little as possible about the model, so the controller specs only need to define what happens when validation passes (or fails) -- and the mock provided by the scaffold does exactly that. If you need to test whether a model instance is valid for a given set of parameters, do that in the model specs.
至于您关于“如何使其更清晰一点”的第一个问题,我们可以从不使用模拟开始。
当您不能依赖辅助对象(一个不重要但必须出现在测试用例中的对象)的可预测行为时,模拟对象非常有用。使用模拟对象的典型示例是数据库查询、网络使用、文件 I/O。您不希望测试因计算机丢失网络连接或数据库不可用而失败。
从
rails gscaffold
生成的代码并不是最佳的。生成的代码的生成方式将使所有测试都通过,为此它使用模拟对象。我不知道他们为什么这样做,我认为更好的默认设置是测试失败,这样你实际上需要做一些事情才能让它们通过。我将删除生成的模拟并执行类似以下操作:
请注意,我们没有使用模拟对象,因此代码被简化为使用一些参数进行 POST 调用并验证该对象是否有效。由于必须在模型文件中写入验证规则,并且您没有使用模拟对象,因此您可以确保正在进行实际验证。
使用自动生成的模拟文件,您根本不需要执行任何操作,测试将永远继续通过。这是一个坏主意,也是一个坏做法。
另请注意,您应该编写更多测试来测试参数无效或丢失的情况。再次,通过执行
assigns(:weather).should_not be_valid
您正在验证您的验证是否正在完成其工作。每次调用
post :create
时编写参数字典是重复的、脆弱的和丑陋的。你应该研究如何使用灯具。例如,Factory Girl 为您提供可重用且更具可读性的代码。
As for your first question about "how to make it a bit clearer", we could begin by not using mocks.
A mock object is useful when you can not depend on a predictable behavior of a secondary object, an object that is not important but must be present in your test case. Typical examples of the usage of mock objects are database queries, network usage, file i/o. You don't want your tests to fail because your computer lost network connection, or a database is not available.
The generated code that you got from the
rails g scaffold
is not optimal. The generated code was generated in a way that will make all tests pass, and for that it uses mock objects. I don't know why they do that, I think a better default would be failing tests so that you actually need to do something to make them pass.I would delete the generated mocks and do something like the following:
Notice that we are not using mock objects, and so the code is reduced to making the POST call with some parameters and verifying that object is valid. Since will have to write the validation rules in your model file, and you are not using a mock object, you can be sure that an actual validation is going on.
With the auto generated mock files you don't have to do anything at all and the tests will continue to pass forever. That is a bad idea, and bad practice.
Also notice that you should write more tests that exercise the cases where there are invalid or missing parameters. And again, by doing
assigns(:weather).should_not be_valid
you are verifying that your validations are doing their job.Writing the dictionary of parameters every time you call
post :create
is repetitive, fragile and ugly. You should study how to use fixtures. For example, with Factory GirlThat gives you reusable and more readable code.