SlideShare a Scribd company logo
Manchester
                                                          October 2009

        Uses & Abuses
      of Mocks & Stubs
       With examples in Ruby using RSpec




                                                         Ashley Moran
Creative Commons Attribution-Noncommercial-Share Alike
2.0 UK: England & Wales License                          PatchSpace Ltd
Presentation goals
To establish a common language about
mock objects
To show some situations where mocks are
useful
To show some situations where mocks are
inappropriate, and problems they can
cause
Presentation non-goals

The slides do not attemptā€¦
 to teach you RSpec
 to cover OO design
 to use real-world code
So please ask questions if I've glossed over
anything you think I should have explained
Basics
Starting Point

A mock is some sort of trivial object that
can be injected into code to make writing
specs easier
A mock can be made to respond to
messages
Traditional Deļ¬nitions
Gerard Meszaros deļ¬nes the following
test object roles, among others:
 dummy
 stub
 fake
 mock
These are precise and signiļ¬cantly
different from each other
RSpec Deļ¬nitions
RSpec provides three (equivalent) factory
methods for creating mock objects

  mock("my mock object")
  stub("my stub object")
  double("my double object")



The RSpec book uses "test double" as the
primary term; I still prefer "mock"
My deļ¬nitions
A mock is any trivial object injected into
your code to help describe its behaviour
A stub method is a method that returns a
canned response
 This may be on a "real" object, more later
A message expectation is an expectation
that fails if a stub method is not called
appropriately in spec code
RSpec concepts
describe "A behaviour description" do
  it "has an example" do
    my_mock = mock(Object,
                  :stub_method => "expected value")
    my_mock.stub_method.should == "expected value"
  end

  it "has an example with a message expectation" do
    my_mock = mock(Object,
                  :expected_method => "value")
    my_mock.should_receive(:expected_method)
    my_mock.expected_method
  end
end
Uses
Deļ¬ning an Interface
Ruby is 99% about objects and interfaces,
and 1% about classes
 describe Person do
   before(:each) do
     @service = mock("service")
     @person = Person.new("Fred", "Flintstone")
   end

   it "can save itself" do
     @service.should_receive(:query).with(
       "STORE 'Fred', 'Flintstone';"
     )
     @person.save(@service)
   end
 end
Deļ¬ning an Interface
The set of options for message
expectations is too large to go into now,
but is extremely ļ¬‚exible
describe Person do
  before(:each) do
    @service = mock("service")
    @person = Person.new("Fred", "Flintstone")
  end

  it "sends relevant details" do
    @service.should_receive(:confirm_family).with(
      hash_including(:name => "Flintstone")
    )
    @person.register(@service)
  end
end
Observable Behaviour
Sometimes you know how your code
should behave even if you ignore the
details of how it works
describe XmlDoc do
  before(:each) do
    body = mock("body", :to_xml => "<body />")
    @doc = XmlDoc.new(body)
  end

  it "renders the document and body" do
    @doc.to_xml.should == "<doc><body /></doc>"
  end
end
Observable Behaviour
This is actually an appropriate situation
for a custom matcher to deliberately
weaken our expectation of the behaviour
  describe XmlDoc do
    # ...

    it "renders the document and body" do
      @doc.to_xml.should be_equivalent_xml_to(
        "<doc><body /></doc>"
      )
    end
  end



I wrote a matcher for this the other day,
but that's another talk
Dependency Injection
The ability to redeļ¬ne methods in Ruby
massively reduces the impact of depending on a
named class compared to eg Java

   class DatabaseLogger
     def initialize(connection_details)
       @connection_details = connection_details
     end

     def log(message)
       Database.new(@connection_details).log(message)
     end
   end
Dependency Injection
 A stub can be used to override factory methods
describe DatabaseLogger do
  before(:each) do
    @connection_details = mock("connection details")
    @database = mock(Database, :log => nil)
    @database_logger = DatabaseLogger.new(@connection_details)
  end

 it "creates a new Database" do
   Database.should_receive(:new).
     with(@connection_details).
     and_return(@database)

    @database_logger.log("take note")
  end
end
Simulating Situations
    Mocks let us put our code through situations
    that may be hard to recreate for real
describe ObjectSaver do
  before(:each) do
    @storage = mock("storage")
    @object_saver = ObjectSaver.new(@storage)
  end

  it "should save" do
    @storage.should_receive(:save).and_raise(OutOfStorageError)
    @object_saver.save(nil)
  end
end


    Especially useful if there's a large number of
    situations (true of some stdlib code)
Degrees of Freedom
        describe Shape do
          before(:each) do
            @triangle = Shape.new(3)
          end

          it "knows how many sides it has" do
            @triangle.sides.should == 3
          end
        end



This Shape class is one dimensional
 Complaints about the examples at the end
 please
Only one thing can vary - will one example do?
Degrees of Freedom
         class Shape
           def initialize(sides)

           end

           def sides
             3
           end
         end




No - one example is not enough
Degrees of Freedom
We need t wo points to prove that the
implementation isn't simplistic

    describe Shape do
      before(:each) do
        @triangle = Shape.new(3)
        @square = Shape.new(4)
      end

      it "knows how many sides it has" do
        @triangle.sides.should == 3
        @square.sides.should == 4
      end
    end
Degrees of Freedom
We now have to solve the problem fully,
but at the expense of more spec code
          class Shape
            def initialize(sides)
              @sides = sides
            end

            def sides
              @sides
            end
          end



This is a trivial example - don't write
getters or use attr_reader in real code!
Degrees of Freedom
We can reduce this to one using a mock
because we can't generate something that
is == the mock from in the code
describe Shape do
  before(:each) do
    @triangle = Shape.new(@sides = mock("sides"))
  end

  it "knows how many sides it has" do
    @triangle.sides.should == @sides
  end
end



This technique is not always appropriate - a
getter method is simply the easiest example
Removing Duplication
Delegation introduces duplication
Consider the following class:


     class Room
       def open_door(key)
         key == "sesame" ? :open : :closed
       end
     end
Removing Duplication
The spec for this code is fairly complex

describe Room do
  before(:each) do
    @room = Room.new
  end

  it "opens with the right key" do
    @room.open_door("sesame").should == :open
  end

  it "won't open with the wrong key" do
    @room.open_door("now!!!").should == :closed
  end
end
Removing Duplication
Imagine a decorator object that delegates
to it
         class Bedsit
           def initialize(room)
             @room = room
           end

           def open_door(key)
             @room.open_door(key)
           end

           def fix_roof
             "it's fixed"
           end
         end
Removing Duplication
How do you write the spec?

describe Bedsit do
  before(:each) do
    @bedsit = Bedsit.new(Room.new)
  end

  it "opens with the right key" do
    # WARNING - duplication!
    @bedsit.open_door("sesame").should == :open
  end

  it "won't open with the wrong key" do
    # ...
  end
end
Removing Duplication
  We actually only care about the delegation
describe Bedsit do
  before(:each) do
    @open_door_response = mock("open_door_response")
    @key = mock("key")
    @room = mock(Room, :open_door => @open_door_response)
    @bedsit = Bedsit.new(@room)
  end

  it "delegates #open_door" do
    @room.should_receive(:open_door).with(@key)
    @bedsit.open_door(@key)
  end

  it "returns the delegated response" do
    @bedsit.open_door(nil).should == @open_door_response
  end
end
Hiding Random Behaviour
 The following code may be useful to
 randomise collections of data

     class Randomiser
       def initialize(collection)
         @collection = collection
       end

       def to_a
         @collection.sort { rand <=> 0.5 }
       end
     end
Hiding Random Behaviour
 This is the client

  class Sampler
    def initialize(values, logger)
      @values, @logger = values, logger
    end

    def sample
      @logger.log(Randomiser.new(@values).to_a[0])
    end
  end
Hiding Random Behaviour
describe Sampler do
  before(:each) do
    @first = mock("first")
    @randomiser = mock(Randomiser, :to_a => [@first, 2, 3])
    Randomiser.stub(:new => @randomiser)

    @logger = mock("logger", :log => nil)
    @values = mock("values")
    @sampler = Sampler.new(@values, @logger)
  end
  it "makes a new Randomiser" do
    Randomiser.should_receive(:new).with(@values)
    @sampler.sample
  end
  it "logs a sample of the data" do
    @logger.should_receive(:log).with(@first)
    @sampler.sample
  end
end
Exploratory Coding
Following the outside in BDD principle leads
you to write client code before provider
code, eg:
 A web controller before a domain model
 A domain model before a storage system
 Data processing before ļ¬le system
 access
I'm going to defer this until the "Mock
Trainwreck" abuse later
Abuses
Modifying the Subject
Modifying the subject of a behaviour
description is dangerous, you can:
 Introduce false positives
 Make the specs brittle
What I call the "subject" is sometimes
known as Object Under Test, Subject Under
Test or System Under Test
Modifying the Subject
This is a stable - albeit incomplete - spec

 describe Person do
   before(:each) do
     @person = Person.new("Fred", "Flintstone")
   end

   it "should have a full name" do
     @person.name.should == "Fred FLINTSTONE"
   end
 end
Modifying the Subject
ā€¦and one of the possible implementations
       class Person
         def initialize(first, last)
           @first, @last = first, last
         end

         def name
           "#{@first} #{last}"
         end

        private

         def last
           @last.upcase
         end
       end
Modifying the Subject
This spec makes assumptions about the
subject

 describe Person do
   before(:each) do
     @person = Person.new("Fred", "Flintstone")
     @person.stub(:last => "FLINTSTONE")
   end

   it "should have a full name" do
     @person.name.should == "Fred FLINTSTONE"
   end
 end
Modifying the Subject
This code silently breaks the behaviour
       class Person
         def initialize(first, last)
           @first, @last = first, last
         end

         def name
           "#{@first} #{last}"
         end

        private

         def last
           @last.downcase
         end
       end


False positives are a catastrophe!
Mock Mania
  describe ManicObject do
    before(:each) do
      @db = mock(Database)
      @logger = mock(Logger)
      @rss_feed = mock(URI)
      @kitchen_sink = mock(KitchenSink)
      @cleaning_solution = mock(FairyLiquid)
    end

    # ...
  end



Specs like this indicate a Single
Responsibility violation. The ManicObject
class is probably highly unstable
Mock Trainwrecks
Stub methods on non-Class objects should
not (usually) return mocks that you
interact with
This indicates a violation of Law of
Demeter in the code
Mock trainwrecks tie your specs to the
current code structure, so are brittle and
scatter the specs for the other objects
Mock Trainwrecks
The code that passes this spec must be
breaking encapsulation to expose its
internals
  describe Train do
    before(:each) do
      @door = mock(Door)
      @cabin = mock(Cabin, :door => @door)
      @engine = mock(Engine, :cabin => @cabin)
      @train = Train.new(@engine)
    end

    it "can lock its door" do
      @door.should_receive(:lock).once
      @train.lock_door
    end
  end
Mock Trainwrecks
 attr_reader shows we're off to a bad start
class Train                   class Cabin
  def initialize(engine)        attr_reader :door
    @engine = engine
  end                           def initialize(door)
                                  @door = door
  def lock_door                 end
    @engine.cabin.door.lock   end
  end
end

class Engine                  class Door
  attr_reader :cabin            def lock
                                  "it's locked"
  def initialize(cabin)         end
    @cabin = cabin            end
  end
end
Mock Trainwrecks
 Let's break this down into single-step
 interactions
describe Train do
  before(:each) do
    @engine = mock(Engine)
    @train = Train.new(@engine)
  end

  it "locks its cabin door" do
    @engine.should_receive(:lock_door).with(no_args)
    @train.lock_door
  end
end
Mock Trainwrecks
The interface to engines is now not
exposed to clients of trains

         class Train
           def initialize(engine)
             @engine = engine
           end

           def lock_door
             @engine.lock_door
           end
         end
Mock Trainwrecks
Apply the pattern recursivelyā€¦

describe Engine do
  before(:each) do
    @cabin = mock(Cabin)
    @engine = Engine.new(@cabin)
  end

  it "locks its cabin door" do
    @cabin.should_receive(:lock_door).with(no_args)
    @engine.lock_door
  end
end
Mock Trainwrecks
The cabin interface is now hidden

          class Engine
            def initialize(cabin)
              @cabin = cabin
            end

            def lock_door
              @cabin.lock_door
            end
          end
Mock Trainwrecks
Apply againā€¦

 describe Cabin do
   before(:each) do
     @door = mock(Door)
     @cabin = Cabin.new(@door)
   end

   it "locks its cabin door" do
     @door.should_receive(:lock).with(no_args)
     @cabin.lock_door
   end
 end
Mock Trainwrecks
The door interface is now hidden

          class Cabin
            def initialize(door)
              @door = door
            end

            def lock_door
              @door.lock
            end
          end
Mock Trainwrecks
Finally we prove that the door actually
locks

   describe Door do
     it "locks" do
       Door.new.lock.should == "it's locked"
     end
   end

   class Door
     def lock
       "it's locked"
     end
   end
Mock Trainwrecks
We've hidden the structure of our object
model, which satisļ¬es the Law of Demeter
But we've now described three internal
interfaces (on Engine, Cabin and Door)
The code can be broken if any internal
interface changes
In an unstable object model, this can lead
to a lot of rework
Mock Trainwrecks
We could eliminate the use of mocks from
most (or all, if we wanted) of the spec
     describe Train do
       before(:each) do
         @door = mock(Door)
         @cabin = Cabin.new(@door)
         @engine = Engine.new(@cabin)
         @train = Train.new(@engine)
       end

       it "can lock its door" do
         @door.should_receive(:lock).once
         @train.lock_door
       end
     end


Be warned that this example is contrived,
and strays far into OO design territory
Mock Trainwrecks
 On the subject of trainwrecks

describe "thetrainline.com" do
  it "should be loved by all" do
    URI.parse("thetrainline.com").should_not be_shit
  end
end




 Unfortunately, Ruby (as of 1.9.1) does not
 deļ¬ne URI#shit? so a custom matcher would
 be required
Summary
Beneļ¬ts of Mocks

Mocks let you explore and drive out new
interfaces needing a full implementation
Mocks let you implement your code
incrementally and get to green faster
Mocks let you prove properties of your
code to the degree of precision and
strength you demand
Trouble from Mocks
Mocks make your specs interface-driven
not behaviour-driven
 Fine if using the interface is the
 behaviour
 Can introduce overhead and rework if
 the interfaces are numerous and trival
 Better to throw away many of these
Mocked specs are more brittle
References

The RSpec Book - David Chelimsky et al
xUnit Patterns - Gerard Meszaros
Mocks Aren't Stubs - Martin Fowler
Mock Roles, not Objects - Steve Freeman
et al
Contact
Slides prepared by Ashley Moran
 ashley.moran@patchspace.co.uk
 www.patchspace.co.uk
I'm available for training/consultancy in
Ruby and RSpec on a freelance basis
 I code for food
Licence


Creative Commons licensed - contact me
for commercial use

More Related Content

What's hot

5 Tips for Better JavaScript
5 Tips for Better JavaScript5 Tips for Better JavaScript
5 Tips for Better JavaScript
Todd Anglin
Ā 
Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 2
Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 2Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 2
Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 2
Philip Schwarz
Ā 
Ruby Gotchas
Ruby GotchasRuby Gotchas
Ruby Gotchas
Dave Aronson
Ā 
Javascript - Tutorial
Javascript - TutorialJavascript - Tutorial
Javascript - Tutorialadelaticleanu
Ā 
Swift internals
Swift internalsSwift internals
Swift internals
Jung Kim
Ā 
What's New In Python 2.6
What's New In Python 2.6What's New In Python 2.6
What's New In Python 2.6
Richard Jones
Ā 
Designing Ruby APIs
Designing Ruby APIsDesigning Ruby APIs
Designing Ruby APIs
Wen-Tien Chang
Ā 
Introduction to Javascript
Introduction to JavascriptIntroduction to Javascript
Introduction to Javascript
Amit Tyagi
Ā 
JavaScript - From Birth To Closure
JavaScript - From Birth To ClosureJavaScript - From Birth To Closure
JavaScript - From Birth To Closure
Robert Nyman
Ā 
DIWE - Working with MySQL Databases
DIWE - Working with MySQL DatabasesDIWE - Working with MySQL Databases
DIWE - Working with MySQL Databases
Rasan Samarasinghe
Ā 
non-strict functions, bottom and scala by-name parameters
non-strict functions, bottom and scala by-name parametersnon-strict functions, bottom and scala by-name parameters
non-strict functions, bottom and scala by-name parameters
Philip Schwarz
Ā 
Does your code spark joy? Refactoring techniques to make your life easier.
Does your code spark joy? Refactoring techniques to make your life easier.Does your code spark joy? Refactoring techniques to make your life easier.
Does your code spark joy? Refactoring techniques to make your life easier.
Juciellen Cabrera
Ā 
A limited guide to intermediate and advanced Ruby
A limited guide to intermediate and advanced RubyA limited guide to intermediate and advanced Ruby
A limited guide to intermediate and advanced Ruby
Vysakh Sreenivasan
Ā 
Objective-c Runtime
Objective-c RuntimeObjective-c Runtime
Objective-c Runtime
Pavel Albitsky
Ā 
Static types on javascript?! Type checking approaches to ensure healthy appli...
Static types on javascript?! Type checking approaches to ensure healthy appli...Static types on javascript?! Type checking approaches to ensure healthy appli...
Static types on javascript?! Type checking approaches to ensure healthy appli...
Arthur Puthin
Ā 
Flex 4 components from the firehose
Flex 4 components from the firehoseFlex 4 components from the firehose
Flex 4 components from the firehose
michael.labriola
Ā 
Talking trash
Talking trashTalking trash
Talking trash
michael.labriola
Ā 
[A 3]Javascript oop for xpages developers - public
[A 3]Javascript oop for xpages developers - public[A 3]Javascript oop for xpages developers - public
[A 3]Javascript oop for xpages developers - public
Kazunori Tatsuki
Ā 
Stamps - a better way to object composition
Stamps - a better way to object compositionStamps - a better way to object composition
Stamps - a better way to object composition
Vasyl Boroviak
Ā 

What's hot (19)

5 Tips for Better JavaScript
5 Tips for Better JavaScript5 Tips for Better JavaScript
5 Tips for Better JavaScript
Ā 
Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 2
Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 2Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 2
Scala 3 by Example - Algebraic Data Types for Domain Driven Design - Part 2
Ā 
Ruby Gotchas
Ruby GotchasRuby Gotchas
Ruby Gotchas
Ā 
Javascript - Tutorial
Javascript - TutorialJavascript - Tutorial
Javascript - Tutorial
Ā 
Swift internals
Swift internalsSwift internals
Swift internals
Ā 
What's New In Python 2.6
What's New In Python 2.6What's New In Python 2.6
What's New In Python 2.6
Ā 
Designing Ruby APIs
Designing Ruby APIsDesigning Ruby APIs
Designing Ruby APIs
Ā 
Introduction to Javascript
Introduction to JavascriptIntroduction to Javascript
Introduction to Javascript
Ā 
JavaScript - From Birth To Closure
JavaScript - From Birth To ClosureJavaScript - From Birth To Closure
JavaScript - From Birth To Closure
Ā 
DIWE - Working with MySQL Databases
DIWE - Working with MySQL DatabasesDIWE - Working with MySQL Databases
DIWE - Working with MySQL Databases
Ā 
non-strict functions, bottom and scala by-name parameters
non-strict functions, bottom and scala by-name parametersnon-strict functions, bottom and scala by-name parameters
non-strict functions, bottom and scala by-name parameters
Ā 
Does your code spark joy? Refactoring techniques to make your life easier.
Does your code spark joy? Refactoring techniques to make your life easier.Does your code spark joy? Refactoring techniques to make your life easier.
Does your code spark joy? Refactoring techniques to make your life easier.
Ā 
A limited guide to intermediate and advanced Ruby
A limited guide to intermediate and advanced RubyA limited guide to intermediate and advanced Ruby
A limited guide to intermediate and advanced Ruby
Ā 
Objective-c Runtime
Objective-c RuntimeObjective-c Runtime
Objective-c Runtime
Ā 
Static types on javascript?! Type checking approaches to ensure healthy appli...
Static types on javascript?! Type checking approaches to ensure healthy appli...Static types on javascript?! Type checking approaches to ensure healthy appli...
Static types on javascript?! Type checking approaches to ensure healthy appli...
Ā 
Flex 4 components from the firehose
Flex 4 components from the firehoseFlex 4 components from the firehose
Flex 4 components from the firehose
Ā 
Talking trash
Talking trashTalking trash
Talking trash
Ā 
[A 3]Javascript oop for xpages developers - public
[A 3]Javascript oop for xpages developers - public[A 3]Javascript oop for xpages developers - public
[A 3]Javascript oop for xpages developers - public
Ā 
Stamps - a better way to object composition
Stamps - a better way to object compositionStamps - a better way to object composition
Stamps - a better way to object composition
Ā 

Similar to Uses & Abuses of Mocks & Stubs

Real life-coffeescript
Real life-coffeescriptReal life-coffeescript
Real life-coffeescript
David Furber
Ā 
PostobjektovƩ programovanie v Ruby
PostobjektovƩ programovanie v RubyPostobjektovƩ programovanie v Ruby
PostobjektovƩ programovanie v Ruby
Jano Suchal
Ā 
Ruby and Rails by example
Ruby and Rails by exampleRuby and Rails by example
Ruby and Rails by example
bryanbibat
Ā 
Introduction to Client-Side Javascript
Introduction to Client-Side JavascriptIntroduction to Client-Side Javascript
Introduction to Client-Side Javascript
Julie Iskander
Ā 
Threequals - Case Equality in Ruby
Threequals - Case Equality in RubyThreequals - Case Equality in Ruby
Threequals - Case Equality in Ruby
Louis Scoras
Ā 
A tour on ruby and friends
A tour on ruby and friendsA tour on ruby and friends
A tour on ruby and friendsę—»ē¦ ę½˜
Ā 
Ruby and Rails by Example (GeekCamp edition)
Ruby and Rails by Example (GeekCamp edition)Ruby and Rails by Example (GeekCamp edition)
Ruby and Rails by Example (GeekCamp edition)
bryanbibat
Ā 
Extreme Swift
Extreme SwiftExtreme Swift
Extreme Swift
Movel
Ā 
ES6 is Nigh
ES6 is NighES6 is Nigh
ES6 is Nigh
Domenic Denicola
Ā 
ppt7
ppt7ppt7
ppt7
callroom
Ā 
ppt2
ppt2ppt2
ppt2
callroom
Ā 
name name2 n
name name2 nname name2 n
name name2 n
callroom
Ā 
ppt9
ppt9ppt9
ppt9
callroom
Ā 
Ruby for Perl Programmers
Ruby for Perl ProgrammersRuby for Perl Programmers
Ruby for Perl Programmers
amiable_indian
Ā 
name name2 n2
name name2 n2name name2 n2
name name2 n2
callroom
Ā 
test ppt
test ppttest ppt
test ppt
callroom
Ā 
name name2 n
name name2 nname name2 n
name name2 n
callroom
Ā 

Similar to Uses & Abuses of Mocks & Stubs (20)

Real life-coffeescript
Real life-coffeescriptReal life-coffeescript
Real life-coffeescript
Ā 
PostobjektovƩ programovanie v Ruby
PostobjektovƩ programovanie v RubyPostobjektovƩ programovanie v Ruby
PostobjektovƩ programovanie v Ruby
Ā 
lab4_php
lab4_phplab4_php
lab4_php
Ā 
lab4_php
lab4_phplab4_php
lab4_php
Ā 
Ruby and Rails by example
Ruby and Rails by exampleRuby and Rails by example
Ruby and Rails by example
Ā 
Introduction to Client-Side Javascript
Introduction to Client-Side JavascriptIntroduction to Client-Side Javascript
Introduction to Client-Side Javascript
Ā 
Threequals - Case Equality in Ruby
Threequals - Case Equality in RubyThreequals - Case Equality in Ruby
Threequals - Case Equality in Ruby
Ā 
Ruby Basics
Ruby BasicsRuby Basics
Ruby Basics
Ā 
A tour on ruby and friends
A tour on ruby and friendsA tour on ruby and friends
A tour on ruby and friends
Ā 
Ruby and Rails by Example (GeekCamp edition)
Ruby and Rails by Example (GeekCamp edition)Ruby and Rails by Example (GeekCamp edition)
Ruby and Rails by Example (GeekCamp edition)
Ā 
Extreme Swift
Extreme SwiftExtreme Swift
Extreme Swift
Ā 
ES6 is Nigh
ES6 is NighES6 is Nigh
ES6 is Nigh
Ā 
ppt7
ppt7ppt7
ppt7
Ā 
ppt2
ppt2ppt2
ppt2
Ā 
name name2 n
name name2 nname name2 n
name name2 n
Ā 
ppt9
ppt9ppt9
ppt9
Ā 
Ruby for Perl Programmers
Ruby for Perl ProgrammersRuby for Perl Programmers
Ruby for Perl Programmers
Ā 
name name2 n2
name name2 n2name name2 n2
name name2 n2
Ā 
test ppt
test ppttest ppt
test ppt
Ā 
name name2 n
name name2 nname name2 n
name name2 n
Ā 

More from PatchSpace Ltd

Conflict in Complex Systems
Conflict in Complex SystemsConflict in Complex Systems
Conflict in Complex Systems
PatchSpace Ltd
Ā 
Personal Kanban (lightning talk)
Personal Kanban (lightning talk)Personal Kanban (lightning talk)
Personal Kanban (lightning talk)
PatchSpace Ltd
Ā 
Parsing for Fun and Profit
Parsing for Fun and ProfitParsing for Fun and Profit
Parsing for Fun and Profit
PatchSpace Ltd
Ā 
Speedy TDD with Rails
Speedy TDD with RailsSpeedy TDD with Rails
Speedy TDD with Rails
PatchSpace Ltd
Ā 
Why Won't My Car Start?
Why Won't My Car Start?Why Won't My Car Start?
Why Won't My Car Start?
PatchSpace Ltd
Ā 
ShRUG 5 - Scottish Ruby Conf edition
ShRUG 5  - Scottish Ruby Conf editionShRUG 5  - Scottish Ruby Conf edition
ShRUG 5 - Scottish Ruby Conf edition
PatchSpace Ltd
Ā 
Encouraging Agile Discipline
Encouraging Agile DisciplineEncouraging Agile Discipline
Encouraging Agile DisciplinePatchSpace Ltd
Ā 
From Specification To Success
From Specification To SuccessFrom Specification To Success
From Specification To SuccessPatchSpace Ltd
Ā 
NWRUG July 2009 - Darcs
NWRUG July 2009 - DarcsNWRUG July 2009 - Darcs
NWRUG July 2009 - DarcsPatchSpace Ltd
Ā 
Elephants In The Meeting Room
Elephants In The Meeting RoomElephants In The Meeting Room
Elephants In The Meeting Room
PatchSpace Ltd
Ā 

More from PatchSpace Ltd (10)

Conflict in Complex Systems
Conflict in Complex SystemsConflict in Complex Systems
Conflict in Complex Systems
Ā 
Personal Kanban (lightning talk)
Personal Kanban (lightning talk)Personal Kanban (lightning talk)
Personal Kanban (lightning talk)
Ā 
Parsing for Fun and Profit
Parsing for Fun and ProfitParsing for Fun and Profit
Parsing for Fun and Profit
Ā 
Speedy TDD with Rails
Speedy TDD with RailsSpeedy TDD with Rails
Speedy TDD with Rails
Ā 
Why Won't My Car Start?
Why Won't My Car Start?Why Won't My Car Start?
Why Won't My Car Start?
Ā 
ShRUG 5 - Scottish Ruby Conf edition
ShRUG 5  - Scottish Ruby Conf editionShRUG 5  - Scottish Ruby Conf edition
ShRUG 5 - Scottish Ruby Conf edition
Ā 
Encouraging Agile Discipline
Encouraging Agile DisciplineEncouraging Agile Discipline
Encouraging Agile Discipline
Ā 
From Specification To Success
From Specification To SuccessFrom Specification To Success
From Specification To Success
Ā 
NWRUG July 2009 - Darcs
NWRUG July 2009 - DarcsNWRUG July 2009 - Darcs
NWRUG July 2009 - Darcs
Ā 
Elephants In The Meeting Room
Elephants In The Meeting RoomElephants In The Meeting Room
Elephants In The Meeting Room
Ā 

Recently uploaded

ODC, Data Fabric and Architecture User Group
ODC, Data Fabric and Architecture User GroupODC, Data Fabric and Architecture User Group
ODC, Data Fabric and Architecture User Group
CatarinaPereira64715
Ā 
Assuring Contact Center Experiences for Your Customers With ThousandEyes
Assuring Contact Center Experiences for Your Customers With ThousandEyesAssuring Contact Center Experiences for Your Customers With ThousandEyes
Assuring Contact Center Experiences for Your Customers With ThousandEyes
ThousandEyes
Ā 
When stars align: studies in data quality, knowledge graphs, and machine lear...
When stars align: studies in data quality, knowledge graphs, and machine lear...When stars align: studies in data quality, knowledge graphs, and machine lear...
When stars align: studies in data quality, knowledge graphs, and machine lear...
Elena Simperl
Ā 
Dev Dives: Train smarter, not harder ā€“ active learning and UiPath LLMs for do...
Dev Dives: Train smarter, not harder ā€“ active learning and UiPath LLMs for do...Dev Dives: Train smarter, not harder ā€“ active learning and UiPath LLMs for do...
Dev Dives: Train smarter, not harder ā€“ active learning and UiPath LLMs for do...
UiPathCommunity
Ā 
The Art of the Pitch: WordPress Relationships and Sales
The Art of the Pitch: WordPress Relationships and SalesThe Art of the Pitch: WordPress Relationships and Sales
The Art of the Pitch: WordPress Relationships and Sales
Laura Byrne
Ā 
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
Sri Ambati
Ā 
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptxIOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
Abida Shariff
Ā 
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
Product School
Ā 
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdfFIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
FIDO Alliance
Ā 
JMeter webinar - integration with InfluxDB and Grafana
JMeter webinar - integration with InfluxDB and GrafanaJMeter webinar - integration with InfluxDB and Grafana
JMeter webinar - integration with InfluxDB and Grafana
RTTS
Ā 
Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...
Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...
Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...
Jeffrey Haguewood
Ā 
Neuro-symbolic is not enough, we need neuro-*semantic*
Neuro-symbolic is not enough, we need neuro-*semantic*Neuro-symbolic is not enough, we need neuro-*semantic*
Neuro-symbolic is not enough, we need neuro-*semantic*
Frank van Harmelen
Ā 
Transcript: Selling digital books in 2024: Insights from industry leaders - T...
Transcript: Selling digital books in 2024: Insights from industry leaders - T...Transcript: Selling digital books in 2024: Insights from industry leaders - T...
Transcript: Selling digital books in 2024: Insights from industry leaders - T...
BookNet Canada
Ā 
Connector Corner: Automate dynamic content and events by pushing a button
Connector Corner: Automate dynamic content and events by pushing a buttonConnector Corner: Automate dynamic content and events by pushing a button
Connector Corner: Automate dynamic content and events by pushing a button
DianaGray10
Ā 
Search and Society: Reimagining Information Access for Radical Futures
Search and Society: Reimagining Information Access for Radical FuturesSearch and Society: Reimagining Information Access for Radical Futures
Search and Society: Reimagining Information Access for Radical Futures
Bhaskar Mitra
Ā 
To Graph or Not to Graph Knowledge Graph Architectures and LLMs
To Graph or Not to Graph Knowledge Graph Architectures and LLMsTo Graph or Not to Graph Knowledge Graph Architectures and LLMs
To Graph or Not to Graph Knowledge Graph Architectures and LLMs
Paul Groth
Ā 
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
DanBrown980551
Ā 
GraphRAG is All You need? LLM & Knowledge Graph
GraphRAG is All You need? LLM & Knowledge GraphGraphRAG is All You need? LLM & Knowledge Graph
GraphRAG is All You need? LLM & Knowledge Graph
Guy Korland
Ā 
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdf
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdfSmart TV Buyer Insights Survey 2024 by 91mobiles.pdf
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdf
91mobiles
Ā 
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
Thierry Lestable
Ā 

Recently uploaded (20)

ODC, Data Fabric and Architecture User Group
ODC, Data Fabric and Architecture User GroupODC, Data Fabric and Architecture User Group
ODC, Data Fabric and Architecture User Group
Ā 
Assuring Contact Center Experiences for Your Customers With ThousandEyes
Assuring Contact Center Experiences for Your Customers With ThousandEyesAssuring Contact Center Experiences for Your Customers With ThousandEyes
Assuring Contact Center Experiences for Your Customers With ThousandEyes
Ā 
When stars align: studies in data quality, knowledge graphs, and machine lear...
When stars align: studies in data quality, knowledge graphs, and machine lear...When stars align: studies in data quality, knowledge graphs, and machine lear...
When stars align: studies in data quality, knowledge graphs, and machine lear...
Ā 
Dev Dives: Train smarter, not harder ā€“ active learning and UiPath LLMs for do...
Dev Dives: Train smarter, not harder ā€“ active learning and UiPath LLMs for do...Dev Dives: Train smarter, not harder ā€“ active learning and UiPath LLMs for do...
Dev Dives: Train smarter, not harder ā€“ active learning and UiPath LLMs for do...
Ā 
The Art of the Pitch: WordPress Relationships and Sales
The Art of the Pitch: WordPress Relationships and SalesThe Art of the Pitch: WordPress Relationships and Sales
The Art of the Pitch: WordPress Relationships and Sales
Ā 
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
Ā 
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptxIOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
Ā 
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
Ā 
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdfFIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
Ā 
JMeter webinar - integration with InfluxDB and Grafana
JMeter webinar - integration with InfluxDB and GrafanaJMeter webinar - integration with InfluxDB and Grafana
JMeter webinar - integration with InfluxDB and Grafana
Ā 
Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...
Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...
Slack (or Teams) Automation for Bonterra Impact Management (fka Social Soluti...
Ā 
Neuro-symbolic is not enough, we need neuro-*semantic*
Neuro-symbolic is not enough, we need neuro-*semantic*Neuro-symbolic is not enough, we need neuro-*semantic*
Neuro-symbolic is not enough, we need neuro-*semantic*
Ā 
Transcript: Selling digital books in 2024: Insights from industry leaders - T...
Transcript: Selling digital books in 2024: Insights from industry leaders - T...Transcript: Selling digital books in 2024: Insights from industry leaders - T...
Transcript: Selling digital books in 2024: Insights from industry leaders - T...
Ā 
Connector Corner: Automate dynamic content and events by pushing a button
Connector Corner: Automate dynamic content and events by pushing a buttonConnector Corner: Automate dynamic content and events by pushing a button
Connector Corner: Automate dynamic content and events by pushing a button
Ā 
Search and Society: Reimagining Information Access for Radical Futures
Search and Society: Reimagining Information Access for Radical FuturesSearch and Society: Reimagining Information Access for Radical Futures
Search and Society: Reimagining Information Access for Radical Futures
Ā 
To Graph or Not to Graph Knowledge Graph Architectures and LLMs
To Graph or Not to Graph Knowledge Graph Architectures and LLMsTo Graph or Not to Graph Knowledge Graph Architectures and LLMs
To Graph or Not to Graph Knowledge Graph Architectures and LLMs
Ā 
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
LF Energy Webinar: Electrical Grid Modelling and Simulation Through PowSyBl -...
Ā 
GraphRAG is All You need? LLM & Knowledge Graph
GraphRAG is All You need? LLM & Knowledge GraphGraphRAG is All You need? LLM & Knowledge Graph
GraphRAG is All You need? LLM & Knowledge Graph
Ā 
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdf
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdfSmart TV Buyer Insights Survey 2024 by 91mobiles.pdf
Smart TV Buyer Insights Survey 2024 by 91mobiles.pdf
Ā 
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
Empowering NextGen Mobility via Large Action Model Infrastructure (LAMI): pav...
Ā 

Uses & Abuses of Mocks & Stubs

  • 1. Manchester October 2009 Uses & Abuses of Mocks & Stubs With examples in Ruby using RSpec Ashley Moran Creative Commons Attribution-Noncommercial-Share Alike 2.0 UK: England & Wales License PatchSpace Ltd
  • 2. Presentation goals To establish a common language about mock objects To show some situations where mocks are useful To show some situations where mocks are inappropriate, and problems they can cause
  • 3. Presentation non-goals The slides do not attemptā€¦ to teach you RSpec to cover OO design to use real-world code So please ask questions if I've glossed over anything you think I should have explained
  • 5. Starting Point A mock is some sort of trivial object that can be injected into code to make writing specs easier A mock can be made to respond to messages
  • 6. Traditional Deļ¬nitions Gerard Meszaros deļ¬nes the following test object roles, among others: dummy stub fake mock These are precise and signiļ¬cantly different from each other
  • 7. RSpec Deļ¬nitions RSpec provides three (equivalent) factory methods for creating mock objects mock("my mock object") stub("my stub object") double("my double object") The RSpec book uses "test double" as the primary term; I still prefer "mock"
  • 8. My deļ¬nitions A mock is any trivial object injected into your code to help describe its behaviour A stub method is a method that returns a canned response This may be on a "real" object, more later A message expectation is an expectation that fails if a stub method is not called appropriately in spec code
  • 9. RSpec concepts describe "A behaviour description" do it "has an example" do my_mock = mock(Object, :stub_method => "expected value") my_mock.stub_method.should == "expected value" end it "has an example with a message expectation" do my_mock = mock(Object, :expected_method => "value") my_mock.should_receive(:expected_method) my_mock.expected_method end end
  • 10. Uses
  • 11. Deļ¬ning an Interface Ruby is 99% about objects and interfaces, and 1% about classes describe Person do before(:each) do @service = mock("service") @person = Person.new("Fred", "Flintstone") end it "can save itself" do @service.should_receive(:query).with( "STORE 'Fred', 'Flintstone';" ) @person.save(@service) end end
  • 12. Deļ¬ning an Interface The set of options for message expectations is too large to go into now, but is extremely ļ¬‚exible describe Person do before(:each) do @service = mock("service") @person = Person.new("Fred", "Flintstone") end it "sends relevant details" do @service.should_receive(:confirm_family).with( hash_including(:name => "Flintstone") ) @person.register(@service) end end
  • 13. Observable Behaviour Sometimes you know how your code should behave even if you ignore the details of how it works describe XmlDoc do before(:each) do body = mock("body", :to_xml => "<body />") @doc = XmlDoc.new(body) end it "renders the document and body" do @doc.to_xml.should == "<doc><body /></doc>" end end
  • 14. Observable Behaviour This is actually an appropriate situation for a custom matcher to deliberately weaken our expectation of the behaviour describe XmlDoc do # ... it "renders the document and body" do @doc.to_xml.should be_equivalent_xml_to( "<doc><body /></doc>" ) end end I wrote a matcher for this the other day, but that's another talk
  • 15. Dependency Injection The ability to redeļ¬ne methods in Ruby massively reduces the impact of depending on a named class compared to eg Java class DatabaseLogger def initialize(connection_details) @connection_details = connection_details end def log(message) Database.new(@connection_details).log(message) end end
  • 16. Dependency Injection A stub can be used to override factory methods describe DatabaseLogger do before(:each) do @connection_details = mock("connection details") @database = mock(Database, :log => nil) @database_logger = DatabaseLogger.new(@connection_details) end it "creates a new Database" do Database.should_receive(:new). with(@connection_details). and_return(@database) @database_logger.log("take note") end end
  • 17. Simulating Situations Mocks let us put our code through situations that may be hard to recreate for real describe ObjectSaver do before(:each) do @storage = mock("storage") @object_saver = ObjectSaver.new(@storage) end it "should save" do @storage.should_receive(:save).and_raise(OutOfStorageError) @object_saver.save(nil) end end Especially useful if there's a large number of situations (true of some stdlib code)
  • 18. Degrees of Freedom describe Shape do before(:each) do @triangle = Shape.new(3) end it "knows how many sides it has" do @triangle.sides.should == 3 end end This Shape class is one dimensional Complaints about the examples at the end please Only one thing can vary - will one example do?
  • 19. Degrees of Freedom class Shape def initialize(sides) end def sides 3 end end No - one example is not enough
  • 20. Degrees of Freedom We need t wo points to prove that the implementation isn't simplistic describe Shape do before(:each) do @triangle = Shape.new(3) @square = Shape.new(4) end it "knows how many sides it has" do @triangle.sides.should == 3 @square.sides.should == 4 end end
  • 21. Degrees of Freedom We now have to solve the problem fully, but at the expense of more spec code class Shape def initialize(sides) @sides = sides end def sides @sides end end This is a trivial example - don't write getters or use attr_reader in real code!
  • 22. Degrees of Freedom We can reduce this to one using a mock because we can't generate something that is == the mock from in the code describe Shape do before(:each) do @triangle = Shape.new(@sides = mock("sides")) end it "knows how many sides it has" do @triangle.sides.should == @sides end end This technique is not always appropriate - a getter method is simply the easiest example
  • 23. Removing Duplication Delegation introduces duplication Consider the following class: class Room def open_door(key) key == "sesame" ? :open : :closed end end
  • 24. Removing Duplication The spec for this code is fairly complex describe Room do before(:each) do @room = Room.new end it "opens with the right key" do @room.open_door("sesame").should == :open end it "won't open with the wrong key" do @room.open_door("now!!!").should == :closed end end
  • 25. Removing Duplication Imagine a decorator object that delegates to it class Bedsit def initialize(room) @room = room end def open_door(key) @room.open_door(key) end def fix_roof "it's fixed" end end
  • 26. Removing Duplication How do you write the spec? describe Bedsit do before(:each) do @bedsit = Bedsit.new(Room.new) end it "opens with the right key" do # WARNING - duplication! @bedsit.open_door("sesame").should == :open end it "won't open with the wrong key" do # ... end end
  • 27. Removing Duplication We actually only care about the delegation describe Bedsit do before(:each) do @open_door_response = mock("open_door_response") @key = mock("key") @room = mock(Room, :open_door => @open_door_response) @bedsit = Bedsit.new(@room) end it "delegates #open_door" do @room.should_receive(:open_door).with(@key) @bedsit.open_door(@key) end it "returns the delegated response" do @bedsit.open_door(nil).should == @open_door_response end end
  • 28. Hiding Random Behaviour The following code may be useful to randomise collections of data class Randomiser def initialize(collection) @collection = collection end def to_a @collection.sort { rand <=> 0.5 } end end
  • 29. Hiding Random Behaviour This is the client class Sampler def initialize(values, logger) @values, @logger = values, logger end def sample @logger.log(Randomiser.new(@values).to_a[0]) end end
  • 30. Hiding Random Behaviour describe Sampler do before(:each) do @first = mock("first") @randomiser = mock(Randomiser, :to_a => [@first, 2, 3]) Randomiser.stub(:new => @randomiser) @logger = mock("logger", :log => nil) @values = mock("values") @sampler = Sampler.new(@values, @logger) end it "makes a new Randomiser" do Randomiser.should_receive(:new).with(@values) @sampler.sample end it "logs a sample of the data" do @logger.should_receive(:log).with(@first) @sampler.sample end end
  • 31. Exploratory Coding Following the outside in BDD principle leads you to write client code before provider code, eg: A web controller before a domain model A domain model before a storage system Data processing before ļ¬le system access I'm going to defer this until the "Mock Trainwreck" abuse later
  • 33. Modifying the Subject Modifying the subject of a behaviour description is dangerous, you can: Introduce false positives Make the specs brittle What I call the "subject" is sometimes known as Object Under Test, Subject Under Test or System Under Test
  • 34. Modifying the Subject This is a stable - albeit incomplete - spec describe Person do before(:each) do @person = Person.new("Fred", "Flintstone") end it "should have a full name" do @person.name.should == "Fred FLINTSTONE" end end
  • 35. Modifying the Subject ā€¦and one of the possible implementations class Person def initialize(first, last) @first, @last = first, last end def name "#{@first} #{last}" end private def last @last.upcase end end
  • 36. Modifying the Subject This spec makes assumptions about the subject describe Person do before(:each) do @person = Person.new("Fred", "Flintstone") @person.stub(:last => "FLINTSTONE") end it "should have a full name" do @person.name.should == "Fred FLINTSTONE" end end
  • 37. Modifying the Subject This code silently breaks the behaviour class Person def initialize(first, last) @first, @last = first, last end def name "#{@first} #{last}" end private def last @last.downcase end end False positives are a catastrophe!
  • 38. Mock Mania describe ManicObject do before(:each) do @db = mock(Database) @logger = mock(Logger) @rss_feed = mock(URI) @kitchen_sink = mock(KitchenSink) @cleaning_solution = mock(FairyLiquid) end # ... end Specs like this indicate a Single Responsibility violation. The ManicObject class is probably highly unstable
  • 39. Mock Trainwrecks Stub methods on non-Class objects should not (usually) return mocks that you interact with This indicates a violation of Law of Demeter in the code Mock trainwrecks tie your specs to the current code structure, so are brittle and scatter the specs for the other objects
  • 40. Mock Trainwrecks The code that passes this spec must be breaking encapsulation to expose its internals describe Train do before(:each) do @door = mock(Door) @cabin = mock(Cabin, :door => @door) @engine = mock(Engine, :cabin => @cabin) @train = Train.new(@engine) end it "can lock its door" do @door.should_receive(:lock).once @train.lock_door end end
  • 41. Mock Trainwrecks attr_reader shows we're off to a bad start class Train class Cabin def initialize(engine) attr_reader :door @engine = engine end def initialize(door) @door = door def lock_door end @engine.cabin.door.lock end end end class Engine class Door attr_reader :cabin def lock "it's locked" def initialize(cabin) end @cabin = cabin end end end
  • 42. Mock Trainwrecks Let's break this down into single-step interactions describe Train do before(:each) do @engine = mock(Engine) @train = Train.new(@engine) end it "locks its cabin door" do @engine.should_receive(:lock_door).with(no_args) @train.lock_door end end
  • 43. Mock Trainwrecks The interface to engines is now not exposed to clients of trains class Train def initialize(engine) @engine = engine end def lock_door @engine.lock_door end end
  • 44. Mock Trainwrecks Apply the pattern recursivelyā€¦ describe Engine do before(:each) do @cabin = mock(Cabin) @engine = Engine.new(@cabin) end it "locks its cabin door" do @cabin.should_receive(:lock_door).with(no_args) @engine.lock_door end end
  • 45. Mock Trainwrecks The cabin interface is now hidden class Engine def initialize(cabin) @cabin = cabin end def lock_door @cabin.lock_door end end
  • 46. Mock Trainwrecks Apply againā€¦ describe Cabin do before(:each) do @door = mock(Door) @cabin = Cabin.new(@door) end it "locks its cabin door" do @door.should_receive(:lock).with(no_args) @cabin.lock_door end end
  • 47. Mock Trainwrecks The door interface is now hidden class Cabin def initialize(door) @door = door end def lock_door @door.lock end end
  • 48. Mock Trainwrecks Finally we prove that the door actually locks describe Door do it "locks" do Door.new.lock.should == "it's locked" end end class Door def lock "it's locked" end end
  • 49. Mock Trainwrecks We've hidden the structure of our object model, which satisļ¬es the Law of Demeter But we've now described three internal interfaces (on Engine, Cabin and Door) The code can be broken if any internal interface changes In an unstable object model, this can lead to a lot of rework
  • 50. Mock Trainwrecks We could eliminate the use of mocks from most (or all, if we wanted) of the spec describe Train do before(:each) do @door = mock(Door) @cabin = Cabin.new(@door) @engine = Engine.new(@cabin) @train = Train.new(@engine) end it "can lock its door" do @door.should_receive(:lock).once @train.lock_door end end Be warned that this example is contrived, and strays far into OO design territory
  • 51. Mock Trainwrecks On the subject of trainwrecks describe "thetrainline.com" do it "should be loved by all" do URI.parse("thetrainline.com").should_not be_shit end end Unfortunately, Ruby (as of 1.9.1) does not deļ¬ne URI#shit? so a custom matcher would be required
  • 53. Beneļ¬ts of Mocks Mocks let you explore and drive out new interfaces needing a full implementation Mocks let you implement your code incrementally and get to green faster Mocks let you prove properties of your code to the degree of precision and strength you demand
  • 54. Trouble from Mocks Mocks make your specs interface-driven not behaviour-driven Fine if using the interface is the behaviour Can introduce overhead and rework if the interfaces are numerous and trival Better to throw away many of these Mocked specs are more brittle
  • 55. References The RSpec Book - David Chelimsky et al xUnit Patterns - Gerard Meszaros Mocks Aren't Stubs - Martin Fowler Mock Roles, not Objects - Steve Freeman et al
  • 56. Contact Slides prepared by Ashley Moran ashley.moran@patchspace.co.uk www.patchspace.co.uk I'm available for training/consultancy in Ruby and RSpec on a freelance basis I code for food
  • 57. Licence Creative Commons licensed - contact me for commercial use