Spec RSpec 模型属性设置器
我正在使用 Sinatra (1.2) 和 RSpec (2.5),并且想创建一个具有属性 TDD 样式的新对象。最终结果应该是这样的:
class User
def initialize(name)
@name = name
end
end
我知道我必须在实现之前编写示例,但我试图在这里解释我的问题。 :) 这是我到目前为止不起作用的规范:
describe User
it "creates a new user object" do
name = mock("A name")
user = mock(User) # shouldn't do this, see the reply's
user.should_receive(:name=).with(name)
User.new(name)
end
end
当我运行 RSpec 时,我收到“预期:1 次,收到 0 次”错误。知道如何解释我想分配 name 属性的 RSpec 吗?
注意:我没有使用 Rails,没有使用 ActiveRecord 或其他任何东西,只是使用 Ruby。
I'm using Sinatra (1.2) and RSpec (2.5) and would like to create a new object with an attribute TDD style. This is how the end result should look like:
class User
def initialize(name)
@name = name
end
end
I know I have to write the example before the implementation but I'm trying to explain my question here. :) Here is the not working spec I have so far:
describe User
it "creates a new user object" do
name = mock("A name")
user = mock(User) # shouldn't do this, see the reply's
user.should_receive(:name=).with(name)
User.new(name)
end
end
When I run RSpec I get the "expected: 1 time, received 0 times" error. Any idea how I can explain RSpec I would like to assign the name attribute?
Note: I'm not using Rails, not using ActiveRecord or anything, just Ruby.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
首先,让我解释一下为什么您编写的规范不起作用:
您设置了一个期望,即
mock(User)
返回的模拟对象应该接收name=
。这有两个问题。首先,模拟不会收到任何信息,因为它从未被调用。mock(User)
返回一个模拟对象,并且它不能用于设置对User
类对象将接收的内容的期望(为此,只需执行User. should_receive(...)
)。其次,即使您在User
类对象上设置了期望,该对象也永远不会收到name=
。这也有两个原因:首先是因为name=
(如果存在)将是一个实例方法,因此不会在类对象上调用,其次,您没有声明name=
实例方法。您的代码所做的是设置一个实例变量。现在,您应该如何为此编写测试?你不应该。测试是为了定义和断言行为,而不是实现。设置实例变量是纯实现。在您的示例代码中,无法从类外部获取
@name
实例变量的值,因此没有理由为其编写测试。显然,您的代码只是一个示例,任何有用的内容都会对
@name
变量执行某些操作,这就是您应该测试的内容。首先为User
对象的用途编写一个测试,然后编写完成该测试所需的所有实现(但仅此而已)。编写一个测试来反映该对象将如何在实际生产代码中使用。First of all, let me explain why the spec you have written doesn't work:
You set an expectation that the mock object returned by
mock(User)
should receivename=
. There are two problems with this. First of all the mock will receive nothing, because it is never called.mock(User)
returns a mock object, and it cannot be used to set expectations for what theUser
class object will receive (to do that simply doUser.should_receive(...)
). Secondly, even if you had set the expectation on theUser
class object, that object will never receivename=
. There are two reasons for this, too: firstly becausename=
(had it existed) would be an instance method, and as such not called on the class object, and secondly, you declare noname=
instance method. What your code does is that it sets an instance variable.Now, how should you write a test for this? You shouldn't. Tests are to define and assert the behaviour, not the implementation. Setting an instance variable is pure implementation. There is in your example code no way to get the value of the
@name
instance variable from outside of the class, therefore there is no reason to write a test for it.Obviously your code is just an example, anything useful would do something with the
@name
variable, and that is what you should test. Start by writing a test for what aUser
object will be used for, then write all the implementation needed to fulfill that test (but no more). Write a test that reflects how the object will be used in actual production code.我真的建议你不要使用模拟来解决这个问题。这不是他们的目的。事实上,像这样指定 getter/setter 并不是 TDD 的真正用途。这个想法是让需求驱动 setter/getter 的存在。例如,可能要求用户的姓名在他/她登录时出现在欢迎消息中。那么您可能会执行以下操作:
这指定了行为,并强制您实现设置名称属性的方法(通过构造函数,在本例中)以及访问它的方法。无需直接指定构造函数。
I'd really recommend you don't approach this using mocks. It's not what they're for. In fact, specifying getters/setters like this is not really what TDD is for. The idea is to let a requirement drive the setters/getters into existence. For example, there might be a requirement that the user's name appear in a welcome message when he/she logs in. Then you might do something like this:
This specifies behavior, and forces you to implement a means of setting the name attribute (via the constructor, in this case) and a means of accessing it. No need to specify the constructor directly.
您真的想模拟您正在开发的对象吗?
Do you really want to mock the very object you are developing?