RSpec: 期望更改多个

在提交特性规范中的表单时,我想检查模型中的许多更改。例如,我希望确保用户名从 X 更改为 Y,并且加密的密码被任何值更改。

我知道已经有一些关于这方面的问题,但我没有找到一个适合我的答案。最准确的答案似乎是由迈克尔约翰斯顿在这里的 ChangeMultiple匹配: Is it possible for RSpec to expect change in two tables?。它的缺点是只检查从已知值到已知值的显式更改。

我创建了一些关于我认为更好的匹配器应该是什么样子的伪代码:

expect {
click_button 'Save'
}.to change_multiple { @user.reload }.with_expectations(
name:               {from: 'donald', to: 'gustav'},
updated_at:         {by: 4},
great_field:        {by_at_leaset: 23},
encrypted_password: true,  # Must change
created_at:         false, # Must not change
some_other_field:   nil    # Doesn't matter, but want to denote here that this field exists
)

我还创建了 ChangeMultiple匹配器的基本框架,如下所示:

module RSpec
module Matchers
def change_multiple(receiver=nil, message=nil, &block)
BuiltIn::ChangeMultiple.new(receiver, message, &block)
end


module BuiltIn
class ChangeMultiple < Change
def with_expectations(expectations)
# What to do here? How do I add the expectations passed as argument?
end
end
end
end
end

但现在我已经得到了这个错误:

 Failure/Error: expect {
You must pass an argument rather than a block to use the provided matcher (nil), or the matcher must implement `supports_block_expectations?`.
# ./spec/features/user/registration/edit_spec.rb:20:in `block (2 levels) in <top (required)>'
# /Users/josh/.rvm/gems/ruby-2.1.0@base/gems/activesupport-4.2.0/lib/active_support/dependencies.rb:268:in `load'
# /Users/josh/.rvm/gems/ruby-2.1.0@base/gems/activesupport-4.2.0/lib/active_support/dependencies.rb:268:in `block in load'

Any help in creating this custom matcher is highly appreciated.

53436 次浏览

在 RSpec 3中,您可以同时设置多个条件(这样就不会破坏单个期望规则)。看起来像这样:

expect {
click_button 'Save'
@user.reload
}.to change { @user.name }.from('donald').to('gustav')
.and change { @user.updated_at }.by(4)
.and change { @user.great_field }.by_at_least(23}
.and change { @user.encrypted_password }

It is not a complete solution though - as far as my research went there is no easy way to do and_not yet. I am also unsure about your last check (if it doesn't matter, why test it?). Naturally you should be able to wrap it within your custom matcher.

如果要测试多个记录没有更改,可以使用 RSpec::Matchers.define_negated_matcher反转匹配器

RSpec::Matchers.define_negated_matcher :not_change, :change

到你的文件顶部(或者到你的 rails_helper.rb) ,然后你可以使用 and链接:

expect{described_class.reorder}.to not_change{ruleset.reload.position}.
and not_change{simple_ruleset.reload.position}

接受的答案不是100% 正确,因为 满了化合物匹配器支持 change {}已经添加到 RSpec 版本3.1.0。如果尝试使用 RSpec 版本3.0运行接受答案中给出的代码,则会得到一个错误。

change {}配合使用复合匹配器有两种方法;

  • 第一个是,你必须至少有 RSpec version 3.1.0
  • Second one is, you have to add def supports_block_expectations?; true; end into the RSpec::Matchers::BuiltIn::Compound class, either by monkey patching it or directly editing the local copy of the gem. 一个重要的提示: this way is not completely equivalent to the first one, the expect {} block runs multiple times in this way!

给你中可以找到完全支持复合匹配器功能的拉请求。

BroiSatse 的回答 是最好的,但是如果您使用 RSpec 2(或者使用更复杂的匹配器,如 .should_not) ,这种方法也可以工作:

lambda {
lambda {
lambda {
lambda {
click_button 'Save'
@user.reload
}.should change {@user.name}.from('donald').to('gustav')
}.should change {@user.updated_at}.by(4)
}.should change {@user.great_field}.by_at_least(23)
}.should change {@user.encrypted_password}