= Flex Mock -- Making Mock Easy

FlexMock is a simple mock object for unit testing.  The interface is
simple, but still provides a good bit of flexibility.

Version :: 0.1.7

= Links

<b>Documents</b>   :: http://onestepback.org/software/flexmock
<b>Download</b>    :: Use RubyGems to download the flexmock gem from http://gems.rubyforge.org

== Installation

You can install FlexMock with the following command.

 $ gem install flexmock

== Simple Example

We have a data acquisition class (+TemperatureSampler+) that reads a
temperature sensor and returns an average of 3 readings.  We don't
have a _real_ temperature to use for testing, so we mock one up with a
mock object that responds to the +read_temperature+ message.

Here's the complete example:

  class TemperatureSampler
    def initialize(sensor)
      @sensor = sensor
    end

    def average_temp
      total = (0...3).collect { @sensor.read_temperature }.inject { |i, s| i + s }
      total / 3.0
    end
  end

  class TestTemperatureSampler < Test::Unit::TestCase
    def test_tempurature_sampler
      readings = [10, 12, 14]
      FlexMock.use("temp") do |sensor|
        sensor = FlexMock.new
        sensor.should_receive(:read_temperature).and_return { readings.shift }
      sampler = TemperatureSampler.new(sensor)
      assert_equal 12, sampler.average_temp
    end
  end

== Quick Reference

The following declarators may be used to create the proper
expectations on a FlexMock object.

* <tt>should_receive(<em>symbol</em>)</tt> -- Declares that a message
  named <em>symbol</em> will be sent to the mock object.  Further
  refinements on this expected message (called an expectation) may be
  chained to the +should_receive+ call.
* <tt>with(<em>arglist</em>)</tt> -- Declares that this expectation
  matches messages that match the given argument list.  The
  <tt>===</tt> operator is used on a argument by argument basis to
  determine matching.  This means that most literal values match
  literally, class values match any instance of a class and regular
  expression match any matching string (after a +to_s+ conversion).
* <tt>with_any_args</tt> -- Declares that this expectation matches the
  message with any argument (default)
* <tt>with_no_args</tt> -- Declares that this expectation matches
  messages with no arguments
* <tt>returns(<em>value</em>)</tt> -- Declares that the message will
  return the given value (<tt>returns(nil)</tt> is the default).
* <tt>returns { code ... }</tt> -- Declares that the message will
  return whatever the block calculates.
* <tt>zero_or_more_times</tt> -- Declares that the message is may be
  sent zero or more times (default, equivalent to
  <tt>at_least.never</tt>).
* <tt>once</tt> -- Declares that the message is only sent once.
  <tt>at_least</tt> / <tt>at_most</tt> modifiers are allowed.
* <tt>twice</tt> -- Declares that the message is only sent twice.
  <tt>at_least</tt> / <tt>at_most</tt> modifiers are allowed.
* <tt>never</tt> -- Declares that the message is never sent.
  <tt>at_least</tt> / <tt>at_most</tt> modifiers are allowed.
* <tt>times(<em>n</em>)</tt> -- Declares that the message is sent
  <em>n</em> times.  <tt>at_least</tt> / <tt>at_most</tt> modifiers
  are allowed.
* <tt>at_least</tt> -- Modifies the immediately following message
  count declarator so that it means the message is sent at least that
  number of times.  E.g. <tt>at_least.once</tt> means the message is
  sent at least once during the test, but may be sent more often.
  Both <tt>at_least</tt> and <tt>at_most</tt> may be specified on the
  same expectation.
* <tt>at_most</tt> -- Similar to <tt>at_least</tt>, but puts an upper
  limit on the number of messages.  Both <tt>at_least</tt> and
  <tt>at_most</tt> may be specified on the same expectation.
* <tt>ordered</tt> -- Declares that the message is ordered and is
  expected to be received in a certain position in a sequence of
  messages.  The message should arrive after and previously declared
  ordered messages and prior to any following declared ordered
  messages.  Unordered messages are ignored when considering the
  message order.
* <tt>ordered(<em>n</em>)</tt> -- Declares that the message is ordered
  with a specific order number.  Order numbers are normally supplied
  sequentially starting with 1.  Explicitly ordered messages must have
  a sequence number greater than the prior implicit order number.
  Using explicit order allows messages to be grouped so that the order
  of messages in a group (sharing an order number) can be received in
  any sequence, but the order between groups is still maintained.  See
  the explicit ordering example below.

== Examples

=== Expect multiple queries and a single update

The queries my have any arguments.  The update must have a specific
argument of 5.

   FlexMock('db').use |db|
     db.should_receive(:query).and_return([1,2,3])
     db.should_recieve(:update).with(5).and_return(nil).once
     # test code here
   end

=== Expect all queries before any updates

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

   FlexMock('db').use |db|
     db.should_receive(:query).and_return([1,2,3]).ordered
     db.should_recieve(:update).and_return(nil).ordered
     # test code here
   end

=== 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 have the same
order number).  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 +with+ method to match different arguement
values to figure out what value to return.

   FlexMock('db').use |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(/^....$/).and_return(3.3).
       at_least.once.ordered(:queries)
     db.should_receive(:finish).once.ordered
     # test code here
   end

=== 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.

   FlexMock('file').use |file|
     return_values = ["line 1\n", "line 2\n"]
     file.should_receive(:gets).with_no_args.and_return { return_values.shift }
     # test code here
   end

=== 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 +should_ignore_missing+ method to turn on missing method
ignoring.

   FlexMock('m').use |m|
     m.should_recieve(:an_important_message).and_return(1).once
     m.should_ignore_missing
     # test code here
   end

<b>Note:</b> The original +mock_ignore_missing+ is now an alias for
+should_ignore_missing+.

== Classic +mock_handle+ Interface

FlexMock still supports the simple +mock_handle+ interface used in the
original version of FlexMock.  +mock_handle+ is equivalent to the
following:

  def mock_handle(sym, expected_count=nil, &block)
    self.should_receive(sym).times(expected_count).returns(&block)
  end

== Other Mock Objects

ruby-mock      :: http://www.b13media.com/dev/ruby/mock.html
test-unit-mock :: http://www.deveiate.org/code/Test-Unit-Mock.shtml

== License

Copyright 2003, 2004, 2005 by Jim Weirich (jim@weirichhouse.org).
All rights reserved.

Permission is granted for use, copying, modification, distribution,
and distribution of modified versions of this work as long as the
above copyright notice is included.


= Other stuff

Author::   Jim Weirich <jim@weirichhouse.org>
Requires:: Ruby 1.8.x or later

== Warranty

This software is provided "as is" and without any express or
implied warranties, including, without limitation, the implied
warranties of merchantibility and fitness for a particular
purpose.
