1 of 61

~ Hamlet, Act 3 Scene 1, as written by a software engineer

To mock, or not to mock, that is the question:

Whether 'tis nobler in the test to verify

The internal state of real objects,

Or to observe the behavior of fake objects

And by mocking, test them.

Emily Giurleo • RubyConf 2021

2 of 61

To be, or not to be?

Emily Giurleo • RubyConf 2021

Shakespeare

3 of 61

To mock, or not to mock?

Emily Giurleo • RubyConf 2021

Me

4 of 61

Emily Giurleo • RubyConf 2021

How do we know when to use a mock?

5 of 61

(aka Emily Giurleo)

Software Engineer at Numero

Co-founder of WNB.rb

Emily Giurleo • RubyConf 2021

Me

6 of 61

Emily Giurleo • RubyConf 2021

Understand what mocking is

Identify scenarios where mocking improves our tests

Articulate a framework for deciding when to mock

Goals

7 of 61

Kind of object

What is verified

Classical

Real

State

Mockist

Fake

Behavior

Emily Giurleo • RubyConf 2021

Names and definitions from “Mocks Aren’t Stubs” by Martin Fowler

8 of 61

16th century inventory manager

Warehouse:

keeps track of inventory

Order:

represents customer order

order.fill(warehouse)

Emily Giurleo • RubyConf 2021

9 of 61

Emily Giurleo • RubyConf 2021

I use rspec + rspec_mocks

(but you should still watch this talk even if you don’t)

10 of 61

Emily Giurleo • RubyConf 2021

Example adapted from “Mocks Aren’t Stubs” by Martin Fowler

Create a real Warehouse object

Pass the Warehouse to Order#fill

Verify state

Add inventory

11 of 61

Emily Giurleo • RubyConf 2021

Example adapted from “Mocks Aren’t Stubs” by Martin Fowler

Create a FAKE Warehouse object

Define expected behavior

Verify behavior

12 of 61

Emily Giurleo • RubyConf 2021

Which is better?

(that’s a trick question)

13 of 61

Emily Giurleo • RubyConf 2021

This breaks when implementation is changed

Example adapted from “Mocks Aren’t Stubs” by Martin Fowler

14 of 61

Emily Giurleo • RubyConf 2021

Example adapted from “Mocks Aren’t Stubs” by Martin Fowler

Fake object

Fake method

Call fake method on fake object

Check that fake method was called on fake object???

15 of 61

Problems with mocking

Emily Giurleo • RubyConf 2021

Tests are deeply coupled to implementation

Tests become self-referential

16 of 61

Benefits of mocking

  1. Mocking prevents the use of expensive resources�
  2. Mocking creates deterministic tests for non-deterministic code�
  3. Mocking allows us to test objects whose state can't/shouldn't be exposed

Emily Giurleo • RubyConf 2021

17 of 61

To mock, or not to mock?

Emily Giurleo • RubyConf 2021

?

?

?

18 of 61

Fakespeare

Emily Giurleo • RubyConf 2021

19 of 61

#generate_quote

Emily Giurleo • RubyConf 2021

Fakespeare

Shakespeare API

identify nouns (ish)

Tempt not a desperate man

replace words

Tempt not a desperate gem

“Tempt not a desperate man”

20 of 61

Act I

Mocking prevents the use of expensive resources

Emily Giurleo • RubyConf 2021

21 of 61

Things that are expensive:

Time

Memory

Money

Emily Giurleo • RubyConf 2021

22 of 61

#generate_quote

Fakespeare

Shakespeare API

identify nouns (ish)

Tempt not a desperate man

replace words

Tempt not a desperate gem

“Tempt not a desperate man”

Emily Giurleo • RubyConf 2021

23 of 61

Time

Money

Emily Giurleo • RubyConf 2021

24 of 61

Mock this?

Emily Giurleo • RubyConf 2021

Test is coupled to third-party library

Test is coupled to API response shape

25 of 61

Client

Client pattern

Network

request

Rest of gem

Emily Giurleo • RubyConf 2021

Give me a quote

“Tempt not a desperate man”

26 of 61

Same as before

Gem doesn’t interact with Net::HTTP

Keep same response shape even if this changes

Emily Giurleo • RubyConf 2021

27 of 61

Emily Giurleo • RubyConf 2021

Mock Shakespeare::Client

Force #new method to return mocked client

Define behavior on mocked client

28 of 61

Emily Giurleo • RubyConf 2021

Does your test use expensive resources?

To mock, or not to mock?

?

?

?

29 of 61

Act II

Mocking creates deterministic tests for non-deterministic code

Emily Giurleo • RubyConf 2021

30 of 61

#generate_quote

Fakespeare

Shakespeare API

identify nouns (ish)

Tempt not a desperate man

replace words

Tempt not a desperate gem

“Tempt not a desperate man”

Emily Giurleo • RubyConf 2021

x

NetworkError

31 of 61

Client

Client pattern

Network

request

Rest of gem

Emily Giurleo • RubyConf 2021

Give me a quote

Shakespeare::Error

32 of 61

Same as other example

Make client raise an error

Emily Giurleo • RubyConf 2021

33 of 61

Side effects

Emily Giurleo • RubyConf 2021

Inputs that are not passed as arguments; can cause non-deterministic behavior

34 of 61

#generate_quote

Fakespeare

Shakespeare API

identify nouns (ish)

Tempt not a desperate man

replace words

Tempt not a desperate gem

“Tempt not a desperate man”

Emily Giurleo • RubyConf 2021

35 of 61

Emily Giurleo • RubyConf 2021

36 of 61

Emily Giurleo • RubyConf 2021

Does your test use expensive resources?

Are you testing a non-deterministic case?

To mock, or not to mock?

?

?

37 of 61

Act III

Mocking helps test objects whose state can’t/shouldn’t be exposed

Emily Giurleo • RubyConf 2021

38 of 61

Cache

Software layer that temporarily stores data for the purpose of fetching it more quickly

Emily Giurleo • RubyConf 2021

39 of 61

Emily Giurleo • RubyConf 2021

Cache

RuffleCollarsRUs.com

RuffleCollarsRUs.com

RuffleCollarsRUs.com

40 of 61

Emily Giurleo • RubyConf 2021

Cache

RuffleCollarsRUs.com

RuffleCollarsRUs.com?

RuffleCollarsRUs.com

41 of 61

#generate_quote

Fakespeare

Shakespeare API

identify nouns (ish)

Tempt not a desperate man

replace words

Tempt not a desperate gem

“Tempt not a desperate man”

Emily Giurleo • RubyConf 2021

x

NetworkError

?

42 of 61

Emily Giurleo • RubyConf 2021

Fakespeare

Shakespeare API

Cache

“Tempt not a desperate man.”

set(“Tempt not a desperate man.”)

43 of 61

Emily Giurleo • RubyConf 2021

Fakespeare

Shakespeare API

Cache

x

“Tempt not a desperate man.”

get

44 of 61

Emily Giurleo • RubyConf 2021

Fakespeare

Shakespeare API

Cache

“Tempt not a desperate man.”

set(“Tempt not a desperate man.”)

45 of 61

Emily Giurleo • RubyConf 2021

The state of a cache is not meant to be examined

46 of 61

Cache

Emily Giurleo • RubyConf 2021

[

“Tempt not a desperate man.”,

“Wisely and slow;

they stumble that run fast.”,

"I will speak daggers to her

but use none.",

]

47 of 61

Fakespeare

Cache

set(“Tempt not a desperate man.”)

Fakespeare

Cache

get

“Tempt not a desperate man.”

Emily Giurleo • RubyConf 2021

48 of 61

Emily Giurleo • RubyConf 2021

Cache

49 of 61

Emily Giurleo • RubyConf 2021

Execute the real method

Check that the method received the correct argument

50 of 61

Emily Giurleo • RubyConf 2021

Fakespeare

Shakespeare API

Cache

x

“Tempt not a desperate man.”

get

51 of 61

Emily Giurleo • RubyConf 2021

The outcome of using the cache looks the same as the outcome of not using it

52 of 61

Emily Giurleo • RubyConf 2021

Define expected behavior

Verify behavior

53 of 61

Verify that cache is not used

Emily Giurleo • RubyConf 2021

54 of 61

Emily Giurleo • RubyConf 2021

Does your test use expensive resources?

Are you testing a non-deterministic case?

Are you testing an object whose state can’t be exposed?

To mock, or not to mock?

?

55 of 61

To mock, or not to mock?

Emily Giurleo • RubyConf 2021

Does your test use expensive resources?

Are you testing a non-deterministic case?

Are you testing an object whose state can’t be exposed?

Mock away!

yes

yes

yes

no

no

no

A pox on your house!

56 of 61

Emily Giurleo • RubyConf 2021

The fault, dear Brutus, is not in our tests, but in our code.

57 of 61

Emily Giurleo

Twitter: @EmilyGiurleo

Emily Giurleo • RubyConf 2021

Website: EmilyGiurleo.dev

58 of 61

Emily Giurleo • RubyConf 2021

https://github.com/egiurleo/fakespeare

59 of 61

Sources

Emily Giurleo • RubyConf 2021

60 of 61

Emily Giurleo • RubyConf 2021

twitter.com/wnb_rb

tinyurl.com/join-wnb-rb

WNB.rb

61 of 61

To mock, or not to mock?

Emily Giurleo • RubyConf 2021

Does your test use expensive resources?

Are you testing a non-deterministic case?

Are you testing an object whose state can’t be exposed?

Mock away!

yes

yes

yes

A pox on your house!

no

no

no