12/08/2018, 14:37

Custom RSpec Matchers.

Đôi khi kết quả mong đợi của một test của bạn quá lớn khiến cho test của bạn mất đi ý nghĩ ban đầu hoặc bạn phải lặp đi lặp lại rất nhiều đoạn mã giống nhau. Chúng ta biết điều này là không tốt, vậy làm thế nào để giải quyết nó? Một cách khá hiệu quả trong trường hợp này là sử dụng các custom ...

Đôi khi kết quả mong đợi của một test của bạn quá lớn khiến cho test của bạn mất đi ý nghĩ ban đầu hoặc bạn phải lặp đi lặp lại rất nhiều đoạn mã giống nhau. Chúng ta biết điều này là không tốt, vậy làm thế nào để giải quyết nó? Một cách khá hiệu quả trong trường hợp này là sử dụng các custom matcher. Một custom matcher cho phép bạn tạo ra những assertions tùy theo yêu cầu của project (mà không có trong các assertions mặc định của rspec). Ngoài ra, custom matcher khá dễ để tạo và sử dụng.

Để tùy chỉnh matcher ta sử dụng gem rspec-expectations (nằm trong gem rspec), nếu bạn muốn sử dụng với các công cụ khác như Test::Unit, Minitest, hoặc Cucumber thì bạn có thể cài trực tiếp: https://github.com/rspec/rspec-expectations

I.Tùy chỉnh rspec matcher.

1.Tạo matcher, tùy chỉnh message, description:

  • Tạo custom matcher với message mặc định:

    • Code tùy chỉnh: Sử dụng phương thức define của RSpec::Matchers
    require 'rspec/expectations'
    
    RSpec::Matchers.define :be_a_multiple_of do |expected|
      match do |actual|
    	actual % expected == 0
      end
    end
    

    Phương thức define extend module RSpec::Matchers::DSL

  • Test example:

    RSpec.describe 9 do
      it { is_expected.to be_a_multiple_of(3) }
    end
    
    RSpec.describe 9 do
      it { is_expected.to be_a_multiple_of(4) }
    end
    
  • Kết quả:

    Then
    the exit status should not be 0
    And
    the output should contain "should be a multiple of 3"
    And
    the output should contain "Failure/Error: it { is_expected.to be_a_multiple_of(4) }"
    And
    the output should contain "2 examples, 1 failures"
    And
    the output should contain "expected 9 to be a multiple of 4"
    
  • Overriding the failure_message

    • Code tùy chỉnh: override failure_message method
    require 'rspec/expectations'
    
    RSpec::Matchers.define :be_a_multiple_of do |expected|
      match do |actual|
    	actual % expected == 0
      end
      failure_message do |actual|
    	"expected that #{actual} would be a multiple of #{expected}"
      end
    end
    
    • Test example
    RSpec.describe 9 do
      	it { is_expected.to be_a_multiple_of(4) }
    end
    
    • Kết quả:
    Then
    the exit status should not be 0
    And
    the stdout should contain "1 example, 1 failure"
    And
    the stdout should contain "expected that 9 would be a multiple of 4"
    
  • Custom description

    • Code tùy chỉnh: override description method
    require 'rspec/expectations'
    
    RSpec::Matchers.define :be_a_multiple_of do |expected|
      match do |actual|
    	actual % expected == 0
      end
      description do
    	"be multiple of #{expected}"
      end
    end
    
    
    • Test example
    RSpec.describe 9 do
      it { is_expected.to be_a_multiple_of(3) }
    end
    
    RSpec.describe 9 do
      it { is_expected.not_to be_a_multiple_of(4) }
    end
    
    • Kết quả:
    Then
    the exit status should be 0
    And
    the stdout should contain "2 examples, 0 failures"
    And
    the stdout should contain "should be multiple of 3"
    And
    the stdout should contain "should not be multiple of 4"
    

2. Tùy chỉnh tham số

  • Không có tham số:
    • Code:
    require 'rspec/expectations'
    
    RSpec::Matchers.define :have_7_fingers do
    	  match do |thing|
    		thing.fingers.length == 7
    	  end
    end
    
    • Test example:
    class Thing
    	  def fingers 
    		(1..7).collect {"finger"} 
    	  end
    end
    
    RSpec.describe Thing do
      	it { is_expected.to have_7_fingers }
    end
    
    • Result:
    	Then
    	the exit status should be 0
    	And
    	the stdout should contain "1 example, 0 failures"
    	And
    	the stdout should contain "should have 7 fingers"
    
  • Có nhiều tham số:
    • Code:
    require 'rspec/expectations'
    
    RSpec::Matchers.define :be_the_sum_of do |a,b,c,d|
      match do |sum|
    	a + b + c + d == sum
      end
    end
    
    • Test example:
    RSpec.describe 10 do
      it { is_expected.to be_the_sum_of(1,2,3,4) }
    end
    
    • Result:
    	Then
    	the exit status should be 0
    	And
    	the stdout should contain "1 example, 0 failures"
    	And
    	the stdout should contain "should be the sum of 1, 2, 3, and 4"
    
    
  • Một khối tham số:
    • Code:
    require 'rspec/expectations'
    
    RSpec::Matchers.define :be_lazily_equal_to do
      match do |obj|
    	obj == block_arg.call
      end
    
      description { "be lazily equal to #{block_arg.call}" }
    end
    
    • Test example:
    RSpec.describe 10 do
      it { is_expected.to be_lazily_equal_to { 10 } }
    end
    
    • Result:
    Then
    the exit status should be 0
    And
    the stdout should contain "1 example, 0 failures"
    And
    the stdout should contain "should be lazily equal to 10"
    

0