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(/^....$/).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(/^....$/) { 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