FlexMock Examples

RSpec.configure do |config|
  config.mock_with :flexmock
end

describe "Simple Spec" do

  # Simple stubbing of some methods

  it "stubs a couple of methods" do
    m = flexmock(:pi => 3.1416, :e => 2.71)
    m.pi.should == 3.1416
    m.e.should == 2.71
  end
end

describe "Returning Undefined" do

  # Create a mock object that returns an undefined object for method calls

  it "returns undefined values" do
    m = flexmock("mock")
    m.should_receive(:divide_by).with(0).
      and_return_undefined

    m.divide_by(0).should == FlexMock.undefined
  end
end

describe "Multiple Queries and Single Updates" do

  # Expect multiple queries and a single update

  # Multiple calls to the query method will be allows, and calls may
  # have any argument list. Each call to query will return the three
  # element array [1, 2, 3]. The call to update must have a specific
  # argument of 5.

  it "queries the db" do
    db = flexmock('db')
    db.should_receive(:query).and_return([1,2,3])
    db.should_receive(:update).with(5).and_return(nil).once

    # Test Code

    db.query
    db.update(5)
  end
end

describe "Ordered Mocks" do

  # Expect all queries before any updates

  # All the query message must occur before any of the update
  # messages.

  it "queries and updates the database" do
    db = flexmock('db')
    db.should_receive(:query).and_return([1,2,3]).ordered
    db.should_receive(:update).and_return(nil).ordered

    # test code here

    db.query
    db.update
  end
end

describe "Ordered Mocks" do

  # Expect several queries with different parameters

  # The queries should happen after startup but before finish. The
  # queries themselves may happen in any order (because they are in
  # the same order group). The first two queries should happen exactly
  # once, but the third query (which matches any query call with a
  # four character parameter) may be called multiple times (but at
  # least once). Startup and finish must also happen exactly once.

  # Also note that we use the <code>with</code> method to match
  # different argument values to figure out what value to return.

  it "queries the database in a particular order" do
    db = flexmock('db')
    db.should_receive(:startup).once.ordered
    db.should_receive(:query).with("CPWR").and_return(12.3).
      once.ordered(:queries)
    db.should_receive(:query).with("MSFT").and_return(10.0).
      once.ordered(:queries)
    db.should_receive(:query).with(%r^....$/).and_return(3.3).
      at_least.once.ordered(:queries)
    db.should_receive(:finish).once.ordered

    # Test Code

    db.startup
    db.query("MSFT")
    db.query("XYZY")
    db.query("CPWR")
    db.finish
  end
end

describe "Ordered Mocks" do

  # Same as above, but using the Record Mode interface

  # The record mode interface offers much the same features as the
  # <code>should_receive</code> interface introduced so far, but it
  # allows the messages to be sent directly to a recording object
  # rather than be specified indirectly using a symbol.

  it "records the queries for replay" do
    db = flexmock('db')
    db.should_expect do |rec|
      rec.startup.once.ordered
      rec.query("CPWR") { 12.3 }.once.ordered(:queries)
      rec.query("MSFT") { 10.0 }.once.ordered(:queries)
      rec.query(%r^....$/) { 3.3 }.at_least.once.ordered(:queries)
      rec.finish.once.ordered
    end

    # Test Code

    db.startup
    db.query("MSFT")
    db.query("XYZY")
    db.query("CPWR")
    db.finish
  end
end

describe "Record Mode" do

  # Using Record Mode to record a known, good algorithm for testing

  # Record mode is nice when you have a known, good algorithm that can
  # use a recording mock object to record the steps. Then you compare
  # the execution of a new algorithm to behavior of the old using the
  # recorded expectations in the mock. For this you probably want to
  # put the recorder in _strict_ mode so that the recorded
  # expectations use exact matching on argument lists, and strict
  # ordering of the method calls.

  # <b>Note:</b> This is most useful when there are no queries on the
  # mock objects, because the query responses cannot be programmed
  # into the recorder object.

  it "compares a know algorithm with a new algorithm" do
    builder = flexmock('builder')
    builder.should_expect do |rec|
      rec.should_be_strict
      known_good_way_to_build_xml(rec)  # record the messages
    end
    new_way_to_build_xml(builder)       # compare to new way
  end

  def known_good_way_to_build_xml(builder)
    builder.person
  end

  def new_way_to_build_xml(builder)
    builder.person
  end

end

describe "Multiple Return Values" do

  # Expect multiple calls, returning a different value each time

  # Sometimes you need to return different values for each call to a
  # mocked method. This example shifts values out of a list for this
  # effect.

  it "returns multiple values" do
    file = flexmock('file')
    file.should_receive(:gets).with_no_args.
      and_return("line 1\n", "line 2\n")

    # test code here

    file.gets                   # returns "line 1"
    file.gets                   # returns "line 2"
  end
end

describe "Ignore Unimportant Messages" do

  # Ignore uninteresting messages

  # Generally you need to mock only those methods that return an
  # interesting value or wish to assert were sent in a particular
  # manner. Use the <code>should_ignore_missing</code> method to turn
  # on missing method ignoring.

  it "ignores unimportant messages" do
    m = flexmock('m')
    m.should_receive(:an_important_message).and_return(1).once
    m.should_ignore_missing

    # Test Code

    m.an_important_message
    m.an_unimportant_message
  end

  # When <code>should_ignore_missing</code> is enabled, ignored
  # missing methods will return an undefined object. Any operation on
  # the undefined object will return the undefined object.

end

describe "Partial Mocks" do

  # Mock just one method on an existing object

  # The Portfolio class calculate the value of a set of stocks by
  # talking to a quote service via a web service. Since we don't want
  # to use a real web service in our unit tests, we will mock the
  # quote service.

  it "returns the portfolio value" do
    flexmock(QuoteService).new_instances do |m|
      m.should_receive(:quote).and_return(100)
    end
    port = Portfolio.new
    value = port.value     # Portfolio calls QuoteService.quote
    value.should == 100
  end

  class QuoteService
  end

  class Portfolio
    def value
      qs = QuoteService.new
      qs.quote
    end
  end
end