【问题标题】:RSpec: Match an array of strings by regexRSpec:通过正则表达式匹配字符串数组
【发布时间】:2010-11-10 15:26:26
【问题描述】:

我正在使用 rspec 测试我的模型的验证,并期待出现错误消息。但是,消息的确切文本可能会发生变化,所以我想更宽容一点,只检查部分消息。

由于 Spec::Matchers::include 方法仅适用于字符串和集合,我目前正在使用此构造:

@user.errors[:password].any?{|m|m.match(/is too short/)}.should be_true

这可行,但对我来说似乎有点麻烦。是否有更好的(即更快或更类似于 ruby​​)的方法来检查数组是否包含正则表达式的字符串,或者可能有一个 rspec 匹配器来执行此操作?

【问题讨论】:

    标签: ruby-on-rails ruby rspec


    【解决方案1】:

    我建议这样做

    @user.errors[:password].to_s.should =~ /is too short/
    

    只是因为它在失败时会给你一个更有帮助的错误。如果您使用be_any,那么您会收到这样的消息...

    Failure/Error: @user.errors[:password].should be_any{ |m| m =~ /is too short/}
        expected any? to return true, got false
    

    但是,如果您使用to_s 方法,那么您将得到如下结果:

     Failure/Error: @user.errors[:password].to_s.should =~ /is too short/
       expected: /is to short/
            got: "[]" (using =~)
       Diff:
       @@ -1,2 +1,2 @@
       -/is too short/
       +"[]"
    

    这样您就可以看到失败的原因,而不必去挖掘很多东西来找出失败的原因。

    【讨论】:

    • 这是我一直使用的版本,效果很好。我同意当您使用 be_any 时难以从模棱两可的错误消息中找出问题所在
    【解决方案2】:

    RSpec 3 expect syntaxmatchers composing 一起使用:

    匹配所有:

    expect(@user.errors[:password]).to all(match /some message/)
    

    匹配任何一个:

    expect(@user.errors[:password]).to include(match /some message/)
    expect(@user.errors[:password]).to include a_string_matching /some message/
    

    【讨论】:

    • 这不太正确。我希望至少一个的密码错误与字符串匹配,但不一定全部
    • 我的错。然后expect(@user.errors[:password]).to include(match /some message/) 或者更好的expect(@user.errors[:password]).to include a_string_matching /some message/
    【解决方案3】:

    您可以将以下代码放在 spec/support/custom_matchers.rb 中

    RSpec::Matchers.define :include_regex do |regex|
      match do |actual|
        actual.find { |str| str =~ regex }
      end
    end
    

    现在你可以像这样使用它了:

    @user.errors.should include_regex(/is_too_short/)
    

    并确保您在 spec/spec_helper.rb 中有类似的内容

    Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
    

    【讨论】:

      【解决方案4】:

      我不认为它会产生性能差异,但更像 RSpec 的解决方案会是

      @user.errors[:password].should be_any { |m| m =~ /is too short/ }
      

      【讨论】:

      • 至于文档,请查看rubydoc.info/gems/rspec-expectations/2.0.1/framesbe_any 是从any? 谓词动态生成的,它来自Enumerable
      • 我可以看到any? 是如何转换成be_any 的,但它的英语听起来不太好。
      • 准确地说,be_any 不是“来自”any?。调用be_any 将动态创建一个Matcher,它将调用any?
      • 我实际上发现这个解决方案在使用一段时间后不太理想,所以我在下面添加了一个新答案。
      【解决方案5】:

      以上两个答案都很好。但是,我会使用较新的 Rspec expect 语法

      @user.errors[:password].to_s.should =~ /is too short/
      

      变成

      expect(@user.errors[:password].to_s).to match(/is too short/)
      

      更多精彩信息在这里:http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax

      【讨论】:

        【解决方案6】:

        我对此的解决方案类似于@muirbot 的。我使用自定义匹配器。但是,我使用真正的 include 匹配器,但使用自定义匹配器作为参数对其进行扩充。在您的套件运行之前将其加载到某个地方(例如,在 spec/support/matchers.rb 中,然后由 spec/spec_helper.rb 加载):

        RSpec::Matchers.define(:a_string_matching) do |expected|
          match do |actual|
            actual =~ expected
          end
        end
        

        那么你的期望可以这样写:

        expect(@user.errors[:password]).to include(a_string_matching(/is too short/))
        

        【讨论】:

        【解决方案7】:

        只是另一种选择

        @user.errors[:password].grep /is too short/
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-10-24
          • 2018-07-26
          • 2012-06-05
          • 2013-12-25
          • 1970-01-01
          相关资源
          最近更新 更多