Upgrade to Pro — share decks privately, control downloads, hide ads and more …

RSpec Expectations and Matchers

RSpec Expectations and Matchers

Dive into RSpec's Expectation DSL, built-in matchers, and custom matchers

Avatar for Stephanie Viccari

Stephanie Viccari

January 04, 2021
Tweet

More Decks by Stephanie Viccari

Other Decks in Education

Transcript

  1. RSpec Expectation Let's break it down: expect(1).to eq(1) expect(1) #

    accepts an argument and represents our "expectation" # create a positive expectation expect(1).to eq(1) # create a negative expectation expect(1).not_to eq(1) expect(1).to_not eq(1) 3
  2. RSpec Matcher expect(1).to eq(1) eq(1) # => accepts an argument

    and represents our "matcher" # => checks if this value matches the expectation value 4
  3. Frequently Used Matchers match_array # confirm two arrays match, regardless

    of ordering expect(["apricots", "mango"]).to match_array(["mango", "apricots"]) --- include # confirm a collection includes one or more expected values. expect(a: 1, b: 2).to include(b: 2) expect(a: 1, b: 2).not_to include(c: 3, d: 4) --- match # confirm a string matches a regex expect(welcome_email.body).to match(/Welcome #{name}/) 6
  4. RSpec Predicate Matchers For any predicate method, RSpec provides a

    corresponding matcher. expect([].empty?).to eq(true) # this use of an equality matcher works but with # predicate matchers, we can make this more readable. expect([]).to be_empty 7
  5. RSpec Predicate Matchers To use a predicate matcher, prefix the

    method with be_ and remove the question mark. expect(user).to be_admin # calls user.admin? expect(5).not_to be_even # calls 5.even? 8
  6. RSpec Predicate Matchers If the predicate matcher begins with has_,

    prefix with have_. expect(hash).to have_key(:foo) # calls hash.has_key?(:foo) 9
  7. RSpec Equality Cheat Sheet equal() # object identity # calls

    equal?() eql() # object equivalence # calls eql?() eq() # object equivalence with type conversion # calls == a = "my string"; b = "my string" expect(a).to equal(a) # => passes expect(a).to equal(b) # => fails a = "my string"; b = "my string" expect(a).to eql(b) # => passes expect(5).to eql(5.0) # => fails a = "my string"; b = "my string" expect(a).to eq(b) # => passes expect(5).to eq(5.0) # => passes 11
  8. Matchers - Gotta Catch them All! Visit the docs to

    start collecting: https://relishapp.com/rspec/rspec-expectations/docs/built-in-matchers Do you have a favorite matcher? 12
  9. Custom RSpec Matchers Goal: Create a matcher that confirms a

    string ends with the letter "y". Example: expect("Sunday").to end_with_y 14
  10. Custom RSpec Matcher: Solution # spec/custom_matcher_spec.rb RSpec::Matchers.define :end_with_y do #

    actual represents the value being examined # (aka, the value passed to `expect`) match do |actual| # `match` expects a block that returns a boolean value actual[-1] == "y" end end RSpec.describe "custom matcher" do it "returns true given a string that ends with the letter 'y'" do expect("Sunday").to end_with_y expect("sunshine").not_to end_with_y end end RSpec Matchers DSL: https://relishapp.com/rspec/rspec-expectations/docs/custom-matchers/define-a-custom-matcher 15
  11. Custom RSpec Matcher - Arguments Goal: Create a matcher that

    confirms a string ends with a specific letter. Example: expect("kerfuffle").to end_with("e") 16
  12. Custom RSpec Matcher - Arguments: Solution # spec/end_with_matcher_spec.rb RSpec::Matchers.define :end_with

    do |matcher| match do |actual| actual[-1] == expected end end RSpec.describe "custom matcher" do it "returns true when the string ends with the specified letter" do expect("kerfuffle").to end_with("e") expect("kerfuffle").not_to end_with("f") end end 17
  13. RSpec Matchers - Suggestions • Use custom matchers sparingly as

    they: • increase indirection • don't include documentation • typically aren't tested • Store matchers within a spec/support/matchers/ directory (example: /spec/matchers/custom-matcher.rb) 18
  14. Real World Example expect(record.created_at).to be_about_now RSpec::Matchers.define :be_about_now do match do

    |actual| expect(actual).to be_within(2.seconds).of(Time.now) end end 19
  15. Resources RSpec Built In Matchers - https://relishapp.com/rspec/rspec- expectations/docs/built-in-matchers RSpec Define

    a Custom Matcher - https://relishapp.com/rspec/rspec- expectations/docs/custom-matchers/ define-a-custom-matcher FactoryBot Matcher - 'be_about_now' - https://github.com/thoughtbot/factorybot/ blob/ eb6bccb3e3cdeef6948cfb7b187e084982318 075/spec/support/matchers/beabout_now.rb 20