4

我正在努力保持我的规范文件尽可能干净。使用 'should' gem 并编写遵循相同模式的自定义匹配器。

我的问题是关于创建一个自定义匹配器,该匹配器可以包装expect{ post :create ... }.to change(Model, :count).by(1)并可以在与其他“应该”匹配器相同的示例组中使用。详情如下:

自定义匹配器(简化)

RSpec::Matchers.define :create_a_new do |model|
  match do |dummy|
    ::RSpec::Expectations::ExpectationTarget.new(subject).to change(model, :count).by(1)
  end
end

工作示例

describe 'POST create:' do
  describe '(valid params)' do
    subject { -> { post :create, model: agency_attributes } }
    it { should create_a_new(Agency) }
  end
end

只要我使用subjectlambda 并且我的匹配器是示例组中唯一的匹配器,这项工作就可以了。

失败的例子

失败示例 1

在同一组中添加更多示例会使其他匹配器失败,因为subject现在是 lambda 而不是 Controller 的实例。

describe 'POST create:' do
  describe '(valid params)' do
    subject { -> { post :create, model: agency_attributes } }
    it { should create_a_new(Agency) }
    it { should redirect_to(Agency.last) }
  end
end

失败示例 2

“应该”匹配器希望我定义一个before块,但这与我的自定义匹配器不兼容

describe 'POST create:' do
  describe '(valid params)' do
    before { post :create, agency: agency_attributes }
    it { should create_a_new(Agency) }
    it { should redirect_to(Agency.last) }
  end
end

预期结果

我正在寻找一种方法来编写我的自定义匹配器,该匹配器将适合与其他匹配器相同的示例组,这意味着我的自定义匹配器应该使用该before块来执行控制器操作,上面的“失败示例 #2”是我想要的方式喜欢写我的规格。可能吗?

谢谢阅读

4

1 回答 1

5

我认为没有办法让你失败的例子通过。

这是因为change确实需要一个 lambda,因为它需要执行两次计数(一次之前,一次在调用它之后)。这就是我倾向于不使用它(或在上下文隔离中使用它)的原因。

我通常做的不是使用count匹配器,而是检查三件事:

  • 记录被保留。如果我将模型分配给@model,那么我使用expect(assigns(:model)).to be_persisted
  • 该记录是预期模型的一个实例(虽然可能看起来没有用,但在使用 STI 时它具有很强的描述性)。expect(assigns(:model)).to be_a(Model).
  • 检查数据库中的最后一条记录是否与我刚刚创建的记录相同`expect(assigns(:model)).to eq(Model.last)`

这就是我通常在change不使用匹配器的情况下测试匹配器的方式。当然,您现在可以创建自己的匹配器

RSpec::Matchers.define :create_a_new do |model|
  match do |actual|
    actual.persisted? &&
      actual.instance_of?(Participant) &&
      (Participant.last == actual)
  end
end
于 2013-08-04T10:08:50.173 回答