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
RSpec: Expect to change multiple
提问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 theRSpec::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, theexpect {}block runs multiple times in this way!
- 第一个是,您必须至少拥有RSpec 版本 3.1.0。
- 第二个是,您必须添加
def supports_block_expectations?; true; end到RSpec::Matchers::BuiltIn::Compound类中,通过猴子修补它或直接编辑 gem 的本地副本。重要提示:这种方式并不完全等同于第一种,expect {}块以这种方式运行多次!
The pull request which added the full support of compound matchers functionality can be found here.
可以在此处找到添加对复合匹配器功能的全面支持的拉取请求。

