Spec RSpec模型属性设置器

时间:2011-04-09 07:36:53

标签: ruby attributes rspec setter

我正在使用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。

3 个答案:

答案 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