Skip to content

Instantly share code, notes, and snippets.

@hpjaj
Created April 2, 2015 16:41
Show Gist options
  • Save hpjaj/ef5ba70a938a963332d0 to your computer and use it in GitHub Desktop.
Save hpjaj/ef5ba70a938a963332d0 to your computer and use it in GitHub Desktop.

Revisions

  1. hpjaj created this gist Apr 2, 2015.
    370 changes: 370 additions & 0 deletions gistfile1.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,370 @@
    ## From Lynda.com course 'RSpec Testing Framework with Ruby'

    describe 'Expectation Matchers' do

    describe 'equivalence matchers' do

    it 'will match loose equality with #eq' do
    a = "2 cats"
    b = "2 cats"
    expect(a).to eq(b)
    expect(a).to be == b # synonym for #eq

    c = 17
    d = 17.0
    expect(c).to eq(d) # different types, but "close enough"
    end

    it 'will match value equality with #eql' do
    a = "2 cats"
    b = "2 cats"
    expect(a).to eql(b) # just a little stricter

    c = 17
    d = 17.0
    expect(c).not_to eql(d) # not the same, close doesn't count
    end

    it 'will match identity equality with #equal' do
    a = "2 cats"
    b = "2 cats"
    expect(a).not_to equal(b) # same value, but different object

    c = b
    expect(b).to equal(c) # same object
    expect(b).to be(c) # synonym for #equal
    end

    end

    describe 'truthiness matchers' do

    it 'will match true/false' do
    expect(1 < 2).to be(true) # do not use 'be_true'
    expect(1 > 2).to be(false) # do not use 'be_false'

    expect('foo').not_to be(true) # the string is not exactly true
    expect(nil).not_to be(false) # nil is not exactly false
    expect(0).not_to be(false) # 0 is not exactly false
    end

    it 'will match truthy/falsey' do
    expect(1 < 2).to be_truthy
    expect(1 > 2).to be_falsey

    expect('foo').to be_truthy # any value counts as true
    expect(nil).to be_falsey # nil counts as false
    expect(0).not_to be_falsey # but 0 is still not falsey enough
    end

    it 'will match nil' do
    expect(nil).to be_nil
    expect(nil).to be(nil) # either way works

    expect(false).not_to be_nil # nil only, just like #nil?
    expect(0).not_to be_nil # nil only, just like #nil?
    end

    end

    describe 'numeric comparison matchers' do

    it 'will match less than/greater than' do
    expect(10).to be > 9
    expect(10).to be >= 10
    expect(10).to be <= 10
    expect(9).to be < 10
    end

    it 'will match numeric ranges' do
    expect(10).to be_between(5, 10).inclusive
    expect(10).not_to be_between(5, 10).exclusive
    expect(10).to be_within(1).of(11)
    expect(5..10).to cover(9)
    end

    end

    describe 'collection matchers' do

    it 'will match arrays' do
    array = [1,2,3]

    expect(array).to include(3)
    expect(array).to include(1,3)

    expect(array).to start_with(1)
    expect(array).to end_with(3)

    expect(array).to match_array([3,2,1])
    expect(array).not_to match_array([1,2])

    expect(array).to contain_exactly(3,2,1) # similar to match_array
    expect(array).not_to contain_exactly(1,2) # but use individual args
    end

    it 'will match strings' do
    string = 'some string'

    expect(string).to include('ring')
    expect(string).to include('so', 'ring')

    expect(string).to start_with('so')
    expect(string).to end_with('ring')
    end

    it 'will match hashes' do
    hash = {:a => 1, :b => 2, :c => 3}

    expect(hash).to include(:a)
    expect(hash).to include(:a => 1)

    expect(hash).to include(:a => 1, :c => 3)
    expect(hash).to include({:a => 1, :c => 3})

    expect(hash).not_to include({'a' => 1, 'c' => 3})
    end

    end

    describe 'other useful matchers' do

    it 'will match strings with a regex' do
    # This matcher is a good way to "spot check" strings
    string = 'The order has been received.'
    expect(string).to match(/order(.+)received/)

    expect('123').to match(/\d{3}/)
    expect(123).not_to match(/\d{3}/) # only works with strings

    email = '[email protected]'
    expect(email).to match(/\A\w+@\w+\.\w{3}\Z/)
    end

    it 'will match object types' do
    expect('test').to be_instance_of(String)
    expect('test').to be_an_instance_of(String) # alias of #be_instance_of

    expect('test').to be_kind_of(String)
    expect('test').to be_a_kind_of(String) # alias of #be_kind_of
    expect('test').to be_a(String) # alias of #be_kind_of
    expect([1,2,3]).to be_an(Array) # alias of #be_kind_of
    end

    it 'will match objects with #respond_to' do
    string = 'test'
    expect(string).to respond_to(:length)
    expect(string).not_to respond_to(:sort)
    end

    it 'will match class instances with #have_attributes' do
    class Car
    attr_accessor :make, :year, :color
    end
    car = Car.new
    car.make = 'Dodge'
    car.year = 2010
    car.color = 'green'

    expect(car).to have_attributes(:color => 'green')
    expect(car).to have_attributes(
    :make => 'Dodge', :year => 2010, :color => 'green'
    )
    end

    it 'will match anything with #satisfy' do
    # This is the most flexible matcher
    expect(10).to satisfy do |value|
    (value >= 5) && (value <=10) && (value % 2 == 0)
    end
    end

    end

    describe 'predicate matchers' do

    it 'will match be_* to custom methods ending in ?' do
    # drops "be_", adds "?" to end, calls method on object
    # Can use these when methods end in "?", require no arguments,
    # and return true/false.

    # with built-in methods
    expect([]).to be_empty # [].empty?
    expect(1).to be_integer # 1.integer?
    expect(0).to be_zero # 0.zero?
    expect(1).to be_nonzero # 1.nonzero?
    expect(1).to be_odd # 1.odd?
    expect(2).to be_even # 1.even?

    # be_nil is actually an example of this too

    # with custom methods
    class Product
    def visible?; true; end
    end
    product = Product.new

    expect(product).to be_visible # product.visible?
    expect(product.visible?).to be true # exactly the same as this
    end

    it 'will match have_* to custom methods like has_*?' do
    # changes "have_" to "has_", adds "?" to end, calls method on object
    # Can use these when methods start with "has_", end in "?",
    # and return true/false. Can have arguments, but not required.

    # with built-in methods
    hash = {:a => 1, :b => 2}
    expect(hash).to have_key(:a) # hash.has_key?
    expect(hash).to have_value(2) # hash.has_value?

    # with custom methods
    class Customer
    def has_pending_order?; true; end
    end
    customer = Customer.new

    expect(customer).to have_pending_order # customer.has_pending_order?
    expect(customer.has_pending_order?).to be true # same as this
    end

    end

    describe 'observation matchers' do
    # Note that all of these use "expect {}", not "expect()".
    # It is a special block format that allows a
    # process to take place inside of the expectation.

    it 'will match when events change object attributes' do
    # calls the test before the block,
    # then again after the block
    array = []
    expect { array << 1 }.to change(array, :empty?).from(true).to(false)

    class WebsiteHits
    attr_accessor :count
    def initialize; @count = 0; end
    def increment; @count += 1; end
    end
    hits = WebsiteHits.new
    expect { hits.increment }.to change(hits, :count).from(0).to(1)
    end

    it 'will match when events change any values' do
    # calls the test before the block,
    # then again after the block

    # notice the "{}" after "change",
    # can be used on simple variables
    x = 10
    expect { x += 1 }.to change {x}.from(10).to(11)
    expect { x += 1 }.to change {x}.by(1)
    expect { x += 1 }.to change {x}.by_at_least(1)
    expect { x += 1 }.to change {x}.by_at_most(1)

    # notice the "{}" after "change",
    # can contain any block of code
    z = 11
    expect { z += 1 }.to change { z % 3 }.from(2).to(0)

    # Must have a value before the block
    # Must change the value inside the block
    end

    it 'will match when errors are raised' do
    # observes any errors raised by the block

    expect { raise StandardError }.to raise_error
    expect { raise StandardError }.to raise_exception

    expect { 1 / 0 }.to raise_error(ZeroDivisionError)
    expect { 1 / 0 }.to raise_error.with_message("divided by 0")
    expect { 1 / 0 }.to raise_error.with_message(/divided/)

    # Note that the negative form does
    # not accept arguments
    expect { 1 / 1 }.not_to raise_error
    end

    it 'will match when output is generated' do
    # observes output sent to $stdout or $stderr

    expect { print('hello') }.to output.to_stdout
    expect { print('hello') }.to output('hello').to_stdout
    expect { print('hello') }.to output(/ll/).to_stdout

    expect { warn('problem') }.to output(/problem/).to_stderr
    end

    end

    describe 'compound expectations' do

    it 'will match using: and, or, &, |' do
    expect([1,2,3,4]).to start_with(1).and end_with(4)

    expect([1,2,3,4]).to start_with(1) & include(2)

    expect(10 * 10).to be_odd.or be > 50

    array = ['hello', 'goodbye'].shuffle
    expect(array.first).to eq("hello") | eq("goodbye")
    end

    end

    describe 'composing matchers' do
    # some matchers accept matchers as arguments. (new in rspec3)

    it 'will match all collection elements using a matcher' do
    array = [1,2,3]
    expect(array).to all( be < 5 )
    end

    it 'will match by sending matchers as arguments to matchers' do
    string = "hello"
    expect { string = "goodbye" }.to change { string }.
    from( match(/ll/) ).to( match(/oo/) )

    hash = {:a => 1, :b => 2, :c => 3}
    expect(hash).to include(:a => be_odd, :b => be_even, :c => be_odd)
    expect(hash).to include(:a => be > 0, :b => be_within(2).of(4))
    end

    it 'will match using noun-phrase aliases for matchers' do
    # These are built-in aliases that make
    # specs read better by using noun-based
    # phrases instead of verb-based phrases.

    # valid but awkward example
    fruits = ['apple', 'banana', 'cherry']
    expect(fruits).to start_with( start_with('a') ) &
    include( match(/a.a.a/) ) &
    end_with( end_with('y') )

    # improved version of the previous example
    # "start_with" becomes "a_string_starting_with"
    # "end_with" becomes "a_string_ending_with"
    # "match" becomes "a_string_matching"
    fruits = ['apple', 'banana', 'cherry']
    expect(fruits).to start_with( a_string_starting_with('a') ) &
    include( a_string_matching(/a.a.a/) ) &
    end_with( a_string_ending_with('y') )


    # valid but awkward example
    array = [1,2,3,4]
    expect(array).to start_with( be <= 2 ) |
    end_with( be_within(1).of(5) )

    # improved version of the previous example
    # "be <= 2" becomes "a_value <= 2"
    # "be_within" becomes "a_value_within"
    array = [1,2,3,4]
    expect(array).to start_with( a_value <= 2 ) |
    end_with( a_value_within(1).of(5) )
    end

    end

    end