RSpec:期望更改多个

Posted

技术标签:

【中文标题】RSpec:期望更改多个【英文标题】:RSpec: Expect to change multiple 【发布时间】:2015-06-05 23:32:51 【问题描述】:

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

我知道已经有一些问题,但我没有找到适合我的答案。最准确的答案似乎是 Michael Johnston 的 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'

非常感谢在创建此自定义匹配器方面的任何帮助。

【问题讨论】:

【参考方案1】:

在 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 

虽然它不是一个完整的解决方案 - 就我的研究而言,还没有简单的方法可以做到 and_not。我也不确定您的最后一次检查(如果没关系,为什么要测试它?)。当然,您应该能够将其包装在您的 custom matcher 中。

【讨论】:

如果您希望多个内容不会更改,只需使用.and change @something .by(0) 你能添加第二个带括号的例子吗?我很难理解哪些方法被链接了 My answer 适用于 Ruby 2,似乎适用于 .should_not 以供任何需要它的人使用【参考方案2】:

如果您想测试多个记录是否未更改,您可以使用RSpec::Matchers.define_negated_matcher 反转匹配器。所以,添加

RSpec::Matchers.define_negated_matcher :not_change, :change

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

expectdescribed_class.reorder.to not_changeruleset.reload.position.
    and not_changesimple_ruleset.reload.position

【讨论】:

【参考方案3】:

BroiSatse's answer 是最好的,但如果您使用的是 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

【讨论】:

啊,好主意!您可能可以构建一个包装器以使其更易于阅读!【参考方案4】:

接受的答案并非 100% 正确,因为 RSpec 版本 3.1.0 中添加了对change 完整 复合匹配器支持。如果您尝试使用 RSpec 3.0 版运行已接受答案中给出的代码,则会收到错误消息。

为了使用change 的复合匹配器,有两种方法;

第一个是,您必须至少拥有 RSpec 版本 3.1.0。 第二个是,您必须将def supports_block_expectations?; true; end 添加到RSpec::Matchers::BuiltIn::Compound 类中,要么通过猴子修补它,要么直接编辑gem 的本地副本。 重要提示:这种方式并不完全等同于第一种方式,expect 块以这种方式运行多次!

可以在here找到完全支持复合匹配器功能的拉取请求。

【讨论】:

以上是关于RSpec:期望更改多个的主要内容,如果未能解决你的问题,请参考以下文章

Rspec期望调用分配(=)方法

如何期望提高 ActiveRecord::RecordNotFound rspec?

Rspec 模拟和存根与期望混淆

RSpec:如何测试 Rails 记录器消息期望?

比较相等的字符串时,RSpec 期望失败

ruby 具有期望语法的Rspec Rails备忘单(包括capybara匹配器)