我正在使用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次”错误。知道如何解释RSpec我想分配名称属性吗?
注意:我没有使用Rails,没有使用ActiveRecord或任何东西,只有Ruby。
答案 0 :(得分:12)
首先,让我解释为什么你写的规范不起作用:
您设置期望mock(User)
返回的模拟对象应该收到name=
。这有两个问题。首先,模拟将不会收到任何东西,因为它永远不会被调用。 mock(User)
返回一个模拟对象,它不能用于设置User
类对象将接收的期望(为此只做User.should_receive(...)
)。其次,即使您在User
类对象上设置了期望值,该对象也永远不会收到name=
。这也有两个原因:首先是因为name=
(如果它存在)将是一个实例方法,因此没有在类对象上调用,其次,你声明没有name=
实例方法。你的代码所做的是它设置一个实例变量。
现在,您应该如何为此编写测试?你不应该。测试是定义和断言行为,而不是实现。设置实例变量是纯实现。在您的示例代码中无法从类外部获取@name
实例变量的值,因此没有理由为其编写测试。
显然,您的代码只是一个示例,任何有用的东西都会对@name
变量执行某些操作,这就是您应该测试的内容。首先编写一个User
对象将用于什么的测试,然后编写完成该测试所需的所有实现(但不多)。编写一个测试,反映对象在实际生产代码中的使用方式。
答案 1 :(得分:5)
我真的建议你不要使用模拟来解决这个问题。这不是他们想要的。事实上,指定这样的getter / setter并不是TDD的真正含义。我们的想法是让一个要求驱动设置者/吸气剂存在。例如,可能要求用户的姓名在他/她登录时显示在欢迎消息中。然后您可能会执行以下操作:
describe 'login process' do
it "displays user's name after successful login" do
user = User.new("Cimm", "cimm@somewhere.com", "secret")
post "/login", :email => "cimm@somewhere.com", :password => "secret"
last_response.body.should =~ /Welcome Cimm/m
end
end
这指定了行为,并强制您实现一种设置name属性的方法(通过构造函数,在本例中)和访问它的方法。无需直接指定构造函数。
答案 2 :(得分:1)
你真的想嘲笑你正在开发的对象吗?
require 'rspec'
class User
attr_accessor :name
def initialize(name)
@name = name
end
end
describe User do
subject {User.new "other name"}
it "creates a new user object" do
subject.should respond_to :name=
end
end