RSpec: Mong đợi thay đổi nhiều


86

Tôi muốn kiểm tra nhiều thay đổi trong mô hình khi gửi biểu mẫu trong thông số tính năng. Ví dụ: tôi muốn đảm bảo rằng tên người dùng đã được thay đổi từ X thành Y và mật khẩu được mã hóa đã được thay đổi theo bất kỳ giá trị nào.

Tôi biết có một số câu hỏi về điều đó, nhưng tôi không tìm thấy câu trả lời phù hợp cho mình. Câu trả lời chính xác nhất có vẻ giống như câu trả lời ChangeMultiplecủa Michael Johnston ở đây: Liệu RSpec có thể mong đợi sự thay đổi trong hai bảng không? . Nhược điểm của nó là chỉ kiểm tra các thay đổi rõ ràng từ các giá trị đã biết sang các giá trị đã biết.

Tôi đã tạo một số mã giả về cách tôi nghĩ rằng một trình đối sánh tốt hơn có thể trông như thế nào:

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
)

Tôi cũng đã tạo khung cơ bản của trình ChangeMultiplekhớp như thế này:

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

Nhưng bây giờ tôi đã gặp lỗi này:

 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'

Bất kỳ trợ giúp nào trong việc tạo đối sánh tùy chỉnh này đều được đánh giá cao.

Câu trả lời:


184

Trong RSpec 3, bạn có thể thiết lập nhiều điều kiện cùng một lúc (để quy tắc kỳ vọng duy nhất không bị phá vỡ). Nó sẽ trông giống như:

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 }

Tuy nhiên, nó không phải là một giải pháp hoàn chỉnh - theo như nghiên cứu của tôi, vẫn chưa có cách nào dễ dàng để thực and_nothiện. Tôi cũng không chắc về lần kiểm tra cuối cùng của bạn (nếu nó không quan trọng, tại sao lại kiểm tra nó?). Đương nhiên, bạn sẽ có thể bọc nó trong trình kết hợp tùy chỉnh của mình .


6
nếu bạn muốn hy vọng rằng nhiều điều được không thay đổi, chỉ cần sử dụng.and change { @something }.by(0)
stevenspiel

1
Bạn có thể thêm một ví dụ thứ hai với tất cả các dấu ngoặc đơn không? Tôi thật khó để hiểu được các phương pháp đang bị xiềng xích
Cyril Duchon-Doris

Câu trả lời của tôi phù hợp với Ruby 2 và dường như phù hợp với .should_notbất kỳ ai cần nó
Zack Morris

37

Nếu bạn muốn kiểm tra rằng nhiều bản ghi không bị thay đổi, bạn có thể đảo ngược trình so khớp bằng cách sử dụng RSpec::Matchers.define_negated_matcher. Vì vậy, hãy thêm

RSpec::Matchers.define_negated_matcher :not_change, :change

lên đầu tệp của bạn (hoặc của bạn rails_helper.rb) và sau đó bạn có thể xâu chuỗi bằng cách sử dụng and:

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

2

Câu trả lời được chấp nhận không đúng 100% vì hỗ trợ trình kết hợp phức hợp đầy đủ cho change {}đã được thêm vào trong phiên bản RSpec 3.1.0 . Nếu bạn cố gắng chạy mã được cung cấp trong câu trả lời được chấp nhận với RSpec phiên bản 3.0, bạn sẽ gặp lỗi.

Để sử dụng các hợp chất so khớp với change {}, có hai cách;

  • Đầu tiên là bạn phải có ít nhất RSpec phiên bản 3.1.0 .
  • Thứ hai là, bạn phải thêm def supports_block_expectations?; true; endvào RSpec::Matchers::BuiltIn::Compoundlớp, bằng cách vá nó hoặc trực tiếp chỉnh sửa bản sao cục bộ của viên ngọc. Một lưu ý quan trọng: cách này không hoàn toàn tương đương với cách đầu tiên, expect {}khối chạy nhiều lần theo cách này!

Bạn có thể tìm thấy yêu cầu kéo bổ sung hỗ trợ đầy đủ chức năng của trình kết hợp phức hợp tại đây .


2

Câu trả lời của BroiSatse là tốt nhất, nhưng nếu bạn đang sử dụng RSpec 2 (hoặc có các trình so khớp phức tạp hơn như .should_not), phương pháp này cũng hoạt động:

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}

1
À, ý kiến ​​hay! Bạn có thể tạo một trình bao bọc để dễ đọc hơn một chút!
BroiSatse
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.