Ruby-on-rails RSpec:期望更改多个

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/29388777/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 00:10:05  来源:igfitidea点击:

RSpec: Expect to change multiple

ruby-on-railsrspecmatcher

提问by Joshua Muheim

I want to check for many changes in a model when submitting a form in a feature spec. For example, I want to make sure that the user name was changed from X to Y, and that the encrypted password was changed by any value.

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

I know there are some questions about that already, but I didn't find a fitting answer for me. The most accurate answer seems like the ChangeMultiplematcher by Michael Johnston here: Is it possible for RSpec to expect change in two tables?. Its downside is that one only check for explicit changes from known values to known values.

我知道已经有一些关于此的问题,但我没有找到适合我的答案。最准确的答案似乎ChangeMultiple是 Michael Johnston 在这里的匹配器:RSpec 是否可能期望在两个表中发生变化?. 它的缺点是只能检查从已知值到已知值的显式变化。

I created some pseudo code on how I think a better matcher could look like:

我创建了一些关于我认为更好的匹配器看起来如何的伪代码:

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
)

I have also created the basic skeleton of the ChangeMultiplematcher like this:

我还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

But now I'm already getting this error:

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

 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.

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

回答by BroiSatse

In RSpec 3 you can setup multiple conditions at once (so the single expectation rule is not broken). It would look sth like:

在 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_notyet. 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.

不过,这不是一个完整的解决方案 - 就我的研究而言,还没有简单的方法可以做到and_not。我也不确定你上次的检查(如果它无关紧要,为什么要测试它?)。自然地,您应该能够将它包装在您的自定义 matcher 中

回答by Matthew Hinea

If you want to test that multiple records were not changed, you can invert a matcher using RSpec::Matchers.define_negated_matcher. So, add

如果要测试多个记录是否未更改,可以使用RSpec::Matchers.define_negated_matcher. 所以,添加

RSpec::Matchers.define_negated_matcher :not_change, :change

to the top of your file (or to your rails_helper.rb) and then you can chain using and:

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

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

回答by Zack Morris

BroiSatse's answeris the best, but if you are using RSpec 2 (or have more complex matchers like .should_not), this method also works:

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}

回答by Foo Bar Zoo

The accepted answer is not 100% correct since the fullcompound matcher support for change {}has been added in RSpec version 3.1.0. If you try to run the code given in accepted answer with the RSpec version 3.0, you would get an error.

接受的答案不是 100% 正确,因为在RSpec 版本 3.1.0 中添加了完整的复合匹配器支持。如果您尝试使用 RSpec 3.0 版运行已接受答案中给出的代码,则会出现错误。change {}

In order to use compound matchers with change {}, there are two ways;

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

  • First one is, you have to have at least RSpec version 3.1.0.
  • Second one is, you have to add def supports_block_expectations?; true; endinto the RSpec::Matchers::BuiltIn::Compoundclass, either by monkey patching it or directly editing the local copy of the gem. An important note:this way is not completely equivalent to the first one, the expect {}block runs multiple times in this way!
  • 第一个是,您必须至少拥有RSpec 版本 3.1.0
  • 第二个是,您必须添加def supports_block_expectations?; true; endRSpec::Matchers::BuiltIn::Compound类中,通过猴子修补它或直接编辑 gem 的本地副本。重要提示:这种方式并不完全等同于第一种,expect {}块以这种方式运行多次!

The pull request which added the full support of compound matchers functionality can be found here.

可以在此处找到添加对复合匹配器功能的全面支持的拉取请求。