How Custom Assertion Matchers Will Keep Mocks Away

I cannot write a series about avoiding mocks without mentioning Custom Assertion Matchers. If you don’t know what custom assertions are, here is pseudo code that uses a custom assertion :

1
assert.that(actual, VerifiesMyCustomAssertion(withCustomProperties))

For more details, have a look at these examples for your preferred language : Java, Ruby or Javascript.

A drawing of a box of matches, branded 'Matchers' on top

That custom assertion matchers have an effect on mock usage might seem puzzling at first. Let me explain. Us, mere human developers, get lured into mocking when tests become too complicated. By keeping the tests simpler, Custom Assertion Matchers help use to avoid mocks. It’s a bit like why test data builders keep mocks at bay.

💡 We get lured into mocking when tests become too complicated

I already blogged about the benefits of Custom Assertion Matchers. Here I’m going to dive in their advantages against mocking.

This is the fifth post in a series about how to avoid mocks. If you haven’t yet, I recommend you to start from the beginning.

Why would we end up with mocks when we don’t have matchers ?

Let’s walkthrough a small story. Suppose we are building an e-commerce website. When someone passes an order, we want to notify the analytics service. Here is some very simple code for that.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class AnalyticsService

 def initialize
   @items = []
 end

 attr_reader :items

 def order_passed(customer, cart)
   cart.each do |item|
     @items.push(customer: customer, item: item)
   end
 end
end

class Order
 def initialize(customer, cart, analytics)
   @customer = customer
   @cart = cart
   @analytics = analytics
 end

 def pass
   # launch order processing and expedition  

   @analytics.order_passed(@customer, @cart)
 end

end

describe 'Order' do

 it "notifies analytics service about passed orders" do
   cart = ["Pasta","Tomatoes"]
   analytics = AnalyticsService.new
   order = Order.new("Philippe", cart, analytics)

   order.pass

   expect(analytics.items).to include(customer: "Philippe", item: "Pasta")
   expect(analytics.items).to include(customer: "Philippe", item: "Tomatoes")
 end
end

Let’s focus on the tests a bit. We first notice that the verification section is large and difficult to understand.  Looking in more details, it knows too much about the internals of AnalyticsService. We had to make the items accessor public just for the sake of testing. The test even knows how the items are stored in a list of hashes. If we were to refactor this representation, we would have to change the tests as well.

We could argue that responsibility-wise, our test should only focus on Order. It makes sense for the test to use a mock to verify that the Order calls AnalyticsService as expected. Let’s see what this would look like.

1
2
3
4
5
6
7
8
9
it "notifies analytics service about passed orders" do
 cart = ["Pasta","Tomatoes"]
 analytics = AnalyticsService.new
 order = Order.new("Philippe", cart, analytics)

 expect(analytics).to receive(:order_passed).with("Philippe", cart)

 order.pass
end

Sure, the test code is simpler. It’s also better according to good design principles. The only glitch is that we now have a mock in place with all the problems I described before.

This might not (yet) be a problem in our example but, for example, the mock ‘cuts’ the execution of the program. Suppose that someday, the Order starts expecting something from the AnalyticsService. We’d then need to ‘simulate’ the real behavior in our mock. This would make the test very hard to maintain.

Matchers to the rescue

Let’s see how a matcher could help us here. The idea is to improve on the first ‘state checking’ solution to make it better than the mock one. We’ll extract and isolate all the state checking code in a custom matcher. By factorizing the code in a single matcher, we’ll reduce duplication. The matcher remains too intimate with the object, but as it is now unique and well named, it’s less of a problem. Plus, as always with matchers, we improved readability.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
RSpec::Matchers.define :have_been_notified_of_order do |customer, cart|
 match do |analytics|
   cart.each do |item|
     return false unless analytics.items.include?(customer: customer, item: item)
   end
   true
 end
end

describe 'Order' do
 it "notifies analytics service about passed orders" do
   cart = ["Pasta","Tomatoes"]
   analytics = AnalyticsService.new
   order = Order.new("Philippe", cart, analytics)

   order.pass

   expect(analytics).to have_been_notified_of_order("Philippe", cart)
 end
end

Here is how we could summarize the pros and cons of each approach :

Assert state Mocks Matchers
👎 duplicated code 👎 duplicates the program behavior ❤️ customizable error messages|
👎 breaks encapsulation ❤️ more readable|
👎 intimacy with the asserted object|
❤️ factorizes the assertion code|

Design improvements

Depending on your situation, you might find further design improvements. In our example, a publish-subscribe pattern might do. A better design is likely to fix the encapsulation problem of the matcher. Here again, the custom assertion matchers will help. In most cases, it will be enough to change the implementation of the matchers only.

💡 Custom assertion matchers make refactoring easier by factorizing test assertions.

Summary of small-scale techniques

I’m done with small scale mock avoiding techniques. To summarize, the first thing to do is to push for more and more immutable value objects. Not only does it help us to avoid mocks, but it will also provides many benefits for production code. Practices like Test Data Builders and Custom Assertion Matchers simplify dealing with Immutable Value Objects in tests. They also help to keep tests small and clean, which is also a great thing against mocks.

Next post

In the following posts, I’ll look into architecture scale techniques to avoid mocks. I’ll start with Hexagonal architecture.

Stay tuned !

How to Use Test Data Builders to Avoid Mocks and Keep Your Tests Clear

We are sometimes tempted to use mocks to shortcut test data initialization. Unfortunately, excessive mocking makes tests difficult to maintain. As Uncle Bob explained, it’s a road that leads to giving up on tests.

Hopefully, Test Data Builders both shortcut test data setup and avoid mocks.

Drawing of a crate

This is the fourth post of a series about how to avoid mocks in automated tests. If you haven’t yet, I recommend you to start from the beginning.

The problem with test data initialization

Setting up the correct state for automated tests can be pretty verbose. This is especially true for software in complex domains or code with a lot of side effects.

The situation gets worse as tests need to setup similar but not exactly identical data. What I often see in code bases is a lot of test data setup duplication. For example, here are tests for a basic ticketing system.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
require 'rspec'
require 'date'

describe 'Ticket Tracking' do
  context "with test setup duplication" do

    it 'latest change date is the creation date when nothing changed' do
      creation_time = DateTime.new(2018,4,26,13,9,0)

      ticket = Ticket.new("Widget broken", "The widget is not loading when ...", "Philippe", creation_time)

      expect(ticket.latest_change).to be(creation_time)
    end

    it 'latest change date is the comment date when a comment is written' do
      ticket = Ticket.new("Widget broken", "The widget is not loading when ...", "Philippe", DateTime.new(2018, 4, 26, 13, 9, 0))
      comment_time = DateTime.new(2018, 4, 26, 13, 16, 0)

      ticket.add_comment(Comment.new("Should work now", "Dan", comment_time))

      expect(ticket.latest_change).to be(comment_time)
    end

    it 'latest change date is the comment date of the latest comment' do
      ticket = Ticket.new("Widget broken", "The widget is not loading when ...", "Philippe", DateTime.new(2018, 4, 26, 13, 9, 0))
      ticket.add_comment(Comment.new("Should work now", "Dan", DateTime.new(2018, 4, 26, 13, 16, 0)))
      comment_time = DateTime.new(2018, 4, 26, 18, 36, 0)

      ticket.add_comment(Comment.new("Should work now", "Dan", comment_time))

      expect(ticket.latest_change).to be(comment_time)
      end

    it 'latest change date is time of latest change if after comment' do
      creation_time = DateTime.new(2018, 4, 26, 13, 9, 0)
      ticket = Ticket.new("Widget broken", "The widget is not loading when ...", "Philippe", creation_time)
      ticket.add_comment(Comment.new("Should work now", "Dan", DateTime.new(2017, 4, 26, 13, 16, 0)))

      expect(ticket.latest_change).to be(creation_time)
    end
  end
end


## The code under test
##
class Ticket

  def initialize(title, description, reporter, creation_time)
    @updated_at = creation_time
    @comments = []
  end

  def latest_change
    ([@updated_at] + @comments.map(&:created_at)).max
  end

  def add_comment(comment)
    @comments.push(comment)
  end
end

class Comment
  attr_reader :created_at
  def initialize(message, author, time)
    @created_at = time
  end
end

It’s clear that there’s a huge amount of duplication in the tests data setups.

The straightforward fix against that is method extraction. This is the Object Mother pattern. Unfortunately, Object Mother breaks down under the number of variations. Every time you need a new change, you’ll add a parameter to the Object Mother method. Long story short, you’ll end up with code like that :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
describe 'Ticket Tracking' do
  context "with object mother" do

    it 'latest change date is the creation date when nothing changed' do
      creation_time = DateTime.new(2018, 4, 26, 13, 5, 0)
      ticket = create_ticket(creation_time, [])

      expect(ticket.latest_change).to be(creation_time)
    end

    it 'latest change date is the comment date when a comment is written' do
      comment_time = DateTime.new(2018, 4, 26, 13, 16, 0)
      ticket = create_ticket(DateTime.new(2018, 4, 26, 13, 9, 0), [comment_time])

      expect(ticket.latest_change).to be(comment_time)
    end

    it 'latest change date is the comment date of the latest comment' do
      comment_time = DateTime.new(2018, 4, 26, 18, 36, 0)
      ticket = create_ticket(DateTime.new(2018, 4, 26, 13, 9, 0),
                             [DateTime.new(2018, 4, 26, 13, 16, 0), comment_time])

      expect(ticket.latest_change).to be(comment_time)
    end

    it 'latest change date is time of latest change if after comment' do
      creation_time = DateTime.new(2018, 4, 26, 13, 9, 0)
      ticket = create_ticket(creation_time,[DateTime.new(2017, 4, 26, 13, 16, 0)])

      expect(ticket.latest_change).to be(creation_time)
    end

    def create_ticket(creation_time, comment_times)
      ticket = Ticket.new("Widget broken", "The widget is not loading when ...", "Philippe", creation_time)
      comment_times.each do |comment_time|
        ticket.add_comment(Comment.new("Should work now", "Dan", comment_time))
      end
      return ticket
    end
  end

end

As you can see, we have less duplication, but the tests got both unreadable and intricate … Following my advices and using more Immutable Value Objects makes the situation worse ! When data is mutable, we can customize it after the call to the Object Mother method. If data is immutable, it all has to be setup at initialization …

That’s when the mock temptation strikes. Sometimes it’s so much easier to mock a method rather than to initialize your data properly. It can be 1 line of mock instead of dealing with all this mess.

💡 If you are not careful, messy test initialization code will trick you into using mocks.

Suppose we now want to make sure we can’t add comments that were written before the ticket was created. We’ll add the following

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
describe 'Ticket Tracking' do
  # ...
  it "is not possible to insert a comment before creation data" do
    ticket = create_ticket(DateTime.new(2018, 4, 26, 13, 9, 0), [])
    expect do
      ticket.add_comment(Comment.new("Should work now", "Dan", DateTime.new(2017, 4, 26, 13, 9, 0)))
    end.to raise_error(ArgumentError)
  end
end
# ...
class Ticket
  # ...
  def add_comment(comment)
    raise ArgumentError unless @updated_at < comment.created_at

    @comments.push(comment)
  end
  # ...
end

Unfortunately, one test (latest change date is time of latest change if after comment) where we were doing just this, will now fail. The fix would be to find a real situation for this test. Here this could be that the ticket is modified after the latest comment. If the tests are too messy though, a mock can be a quick and dirty fix the setup and make the test pass :

1
2
3
4
5
6
7
8
9
it 'latest change date is time of latest change if after comment' do
  creation_time = DateTime.new(2018, 4, 26, 13, 9, 0)
  ticket = create_ticket(creation_time, [])
  comment = Comment.new("Should work now", "Dan", DateTime.new(2018, 4, 26, 13, 16, 0))
  ticket.add_comment(comment)
  allow(comment).to receive(:created_at).and_return(DateTime.new(2017, 4, 26, 13, 16, 0))

  expect(ticket.latest_change).to be(creation_time)
end

There is a third way : Test Data Builders

What are test data builders

As often, when design is not satisfying, adding an indirection solves the issue. Here the indirection takes shape of the Builder pattern.

Builder Pattern [Wikipedia] :

The intent of the Builder design pattern is to separate the construction of a complex object from its representation. By doing so the same construction process can create different representations.

The idea is to use the builder pattern to build the test data. Growing Object Oriented Software Guided by Tests covers this technique in great length.

Cover of the book Growing Object Oriented Software Guided By Tests

Here is the previous code re-written using the test data builder pattern.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
require 'rspec'
require 'date'

describe 'Ticket Tracking' do
  context "with test data builders" do

    before :each do
      @t = date_times.build
    end

    it 'latest change date is the creation date when nothing changed' do
      ticket = a_ticket.at(@t[0]).build

      expect(ticket.latest_change).to be(@t[0])
    end

    it 'latest change date is the comment date when a comment is written' do
      ticket = a_ticket
                   .at(@t[0])
                   .with_comment(a_comment.at(@t[1]))
                   .build

      expect(ticket.latest_change).to be(@t[1])
    end

    it 'latest change date is the comment date of the latest comment' do
      ticket = a_ticket
                   .at(@t[0])
                   .with_comment(a_comment.at(@t[1]))
                   .with_comment(a_comment.at(@t[2]))
                   .build

      expect(ticket.latest_change).to be(@t[2])
    end

    it 'latest change date is time of latest change if after comment' do
      ticket = a_ticket.at(@t[0])
                   .with_comment(a_comment.at(@t[1]))
                   .build

      ticket.update_description("The widget is not loading when logged in as anonymous", @t[2])

      expect(ticket.latest_change).to be(@t[2])
    end

    it "is not possible to insert a comment before creation data" do
      ticket = a_ticket.at(@t[1]).build

      expect do
        ticket.add_comment(a_comment.at(@t[0]).build)
      end.to raise_error(ArgumentError)
    end
  end
end

## Test Data Builders
##
class DateTimeBuilder
  def build
    seed = DateTime.now
    (0..10).map {|i| seed + i}
  end
end
def date_times()
  DateTimeBuilder.new
end

class CommentBuilder
  def initialize
    @at = DateTime.now
  end
  def at(time)
    @at = time
    self
  end
  def build
    Comment.new("Should work now", "Dan", @at)
  end
end
def a_comment()
  CommentBuilder.new
end

class TicketBuilder
  def initialize
    @at = DateTime.now
    @comments = []
  end
  def at(time)
    @at = time
    self
  end
  def with_comment(comment_builder)
    @comments.push(comment_builder.build)
    self
  end
  def build
    ticket = Ticket.new("Widget broken", "The widget is not loading when ...", "Philippe", @at)
    @comments.each do |comment|
      ticket.add_comment(comment)
    end
    ticket
  end
end
def a_ticket()
  TicketBuilder.new
end

## The code under test
##
class Ticket

  def initialize(title, description, reporter, creation_time)
    @updated_at = creation_time
    @comments = []
  end

  def latest_change
    ([@updated_at] + @comments.map(&:created_at)).max
  end

  def add_comment(comment)
    raise ArgumentError unless @updated_at < comment.created_at

    @comments.push(comment)
  end

  def update_description(description, update_time)
    @updated_at = update_time
  end
end

class Comment
  attr_reader :created_at

  def initialize(message, author, time)
    @created_at = time
  end
end

As you can see, it provides default test values, and we only need to provide the custom values we care about. This makes the test code both readable and intention revealing. Making the tests more understandable helps a lot to find ways to avoid mocks. Here, we replaced the mock on the comment time by an update to the ticket after the last comment.

The pattern applies in many languages, even if implementations will be different. In Ruby, libraries like factory_bot avoid a lot of boilerplate code. Have a look at this article for examples in Java.

Other advantages

Test data builders have another second effect benefit. When setting up the data is complicated, we are likely to add more that one assertion in a test. Unit tests can end up looking like a mini scenario to avoid duplicating this test setup.

It’s easy to create a specific tests for every assertion with Test Data Builders. By doing so we get smaller and more focused tests, which bring :

  • Better names for tests
  • More readable tests
  • Faster diagnostic of the problem when a particular test fails
  • 🎁 Better coverage ! In a large test, all assertions work on the same input values. When we have many small tests, we can use a different value in each.

💡 By simplifying the creation of new tests with different data, Test Data Builders increase code coverage in the long term!

Next week

This is the fourth post of a series about how to avoid mocks in automated tests. Next week I’ll dig into Custom Assertion Matchers and how they avoid mock expectations.

Stay tuned ! !

Immutable Value Objects vs Mocks : Fizz Buzz

In my previous post I explained how Immutable Value Objects help us to avoid mocks. In this post, I’ll illustrate this in practice with real code.

This is the third post on a series about how to avoid mocks. If you haven’t, you can start reading the full story here.

A drawing "FIZZ BUZZ" rock fallen and sealed in the ground

Fizz Buzz Example

As a simple example, I’ll go through the classic Fizz Buzz. I’ve implemented and tested it with and without immutable value objects. Please keep in mind that this is a toy example, where problems are obvious and easily fixed. I try to highlight at small scale the same problems that get hidden by the complexity of a large scale program.

Let’s start with a typical FizzBuzz implementation.

1
2
3
4
5
6
7
8
9
10
11
1.upto(100) do |i|
  if (i%3 == 0 and i%5 == 0)
    STDOUT.puts("FizzBuzz\n")
  elsif (i%3 == 0)
    STDOUT.puts("Fizz\n")
  elsif (i%5 == 0)
    STDOUT.puts("Buzz\n")
  else
    STDOUT.puts("#{i}\n")
  end
end

Suppose you need to add some tests around the code. The most straightforward way is to mock STDOUT :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
require 'rspec'

def fizzBuzz(max, out)
  1.upto(max) do |i|
    if (i%3 == 0 and i%5 == 0)
      out.puts("FizzBuzz\n")
    elsif (i%3 == 0)
      out.puts("Fizz\n")
    elsif (i%5 == 0)
      out.puts("Buzz\n")
    else
      out.puts("#{i}\n")
    end
  end
end

# main
fizzBuzz(100,STDOUT)

describe 'Mockist Fizz Buzz' do

  it 'should print numbers, fizz and buzz' do
    out = double("out")
    expect(out).to receive(:puts).with("1\n").ordered
    expect(out).to receive(:puts).with("2\n").ordered
    expect(out).to receive(:puts).with("Fizz\n").ordered
    expect(out).to receive(:puts).with("4\n").ordered
    expect(out).to receive(:puts).with("Buzz\n").ordered
    expect(out).to receive(:puts).with("Fizz\n").ordered
    expect(out).to receive(:puts).with("7\n").ordered
    expect(out).to receive(:puts).with("8\n").ordered
    expect(out).to receive(:puts).with("Fizz\n").ordered
    expect(out).to receive(:puts).with("Buzz\n").ordered
    expect(out).to receive(:puts).with("11\n").ordered
    expect(out).to receive(:puts).with("Fizz\n").ordered
    expect(out).to receive(:puts).with("13\n").ordered
    expect(out).to receive(:puts).with("14\n").ordered
    expect(out).to receive(:puts).with("FizzBuzz\n").ordered

    fizzBuzz(15, out)
  end
end

Unfortunately, there are a few problems with this code :

  • With nested logic and complicated mock setup, both code and tests aren’t very readable
  • They both seem to violate the single responsibility principle as well
  • It’s depending on a mutable output. Within a larger program, something could be messing around with this output stream. That would break FizzBuzz.

Let’s now try to use as many immutable values objects as possible, and see what happens to the mocks.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
require 'rspec'

# We extracted a function to do the fizz buzz on a single number
def fizzBuzzN(i)
  if (i%3 == 0 and i%5 == 0)
    "FizzBuzz"
  elsif (i%3 == 0)
    "Fizz"
  elsif (i%5 == 0)
    "Buzz"
  else
    i.to_s
  end
end

# We replaced the many calls to STDOUT.puts by building a single 
# large (and immutable) string
def fizzBuzz(max)
  ((1..max).map {|i| fizzBuzzN(i)}).join("\n")
end

# main, with a single call to STDOUT.puts
STDOUT.puts fizzBuzz(100)

describe 'Statist Fizz Buzz' do

  it 'should print numbers not multiples of 3 or 5' do
    expect(fizzBuzzN(1)).to eq("1")
    expect(fizzBuzzN(2)).to eq("2")
    expect(fizzBuzzN(4)).to eq("4")
  end

  it 'should print Fizz for multiples of 3' do
    expect(fizzBuzzN(3)).to eq("Fizz")
    expect(fizzBuzzN(6)).to eq("Fizz")
  end

  it 'should print Buzz for multiples of 5' do
    expect(fizzBuzzN(5)).to eq("Buzz")
    expect(fizzBuzzN(10)).to eq("Buzz")
  end

  it 'should print FizzBuzz for multiples of 3 and 5' do
    expect(fizzBuzzN(15)).to eq("FizzBuzz")
    expect(fizzBuzzN(30)).to eq("FizzBuzz")
  end


  it 'should print numbers, fizz and buzz' do
    expect(fizzBuzz(15)).to start_with("1\n2\nFizz").and(end_with("14\nFizzBuzz"))
  end
end

As we can see, using immutable value objects got us rid of the mocks. Obviously, this new code will not be as efficient as the original version, but most of the time, this does not matter. As a bonus though we get finer grain and more readable tests.

Other testing advantages

Appart from preventing mocks, Immutable Value Objects have other advantages related to testing.

  • We can directly assert their equality, without having to dig into their guts
  • We can call methods as many times as we want, without the risk of changing anything and breaking the tests
  • Immutable Value Objects are a lot less likely to contain invalid state. This removes the need for a whole range of validity tests.

💡 Immutable Value Objects simplify testing in many ways.

Convincing your teammates

We’ve seen that Immutable Value Objects have a ton of advantages when testing. People have found that they also have many other benefits :

Surprisingly though, it’s difficult to persuade programmers to use more immutability. It’s tricky to explain why returning a modified copy is simpler than just adding a setter.

💡 Why is it so hard to persuade other developers to use immutable data structures ?

I had the most success by far when encountering a bug resulting of share mutable state. When this happens, the long term benefits and safety of the immutable design wins people over. The good thing is that as you convince more people in the team, immutability will spread like a virus !

Outside of this situation, you might try some of the following arguments to move people :

  • Immutable values prevent bugs caused by different parts of the system changing the same mutable state
  • They make it easier to deal with the program in smaller parts and to reason about the system in general
  • Immutable values don’t need any synchronization and make multithreaded programming easier
  • When tempted to add a simple setter instead of keeping a class immutable, highlight the stressful debugging time to come
  • If you’re dealing with a Design By Contract adept, explain how immutability has it built-in
  • Admit that mainstream languages have bad support for Immutable Value. Point to patterns like Data Builders that work around these limitation

Next post

I’m done with immutable value objects. It was a far longer post than I thought, but there was a lot to say. This was the third post in a series about avoiding mocks. In next post, I’ll dig into another small scale mock fighting pattern : Test Data Builders.

How Immutable Value Objects Fight Mocks

Excessive use of mocks makes tests very painful to maintain. If we stick painful mocks for too long, we’ll end up abandoning unit testing. Eventually, the system will degrade into legacy. 

There are many techniques to avoid mocks. Some of the most effective involve architecture changes. Unfortunately, there are not the most straightforward to use. Re-architecting involves people and time that you may not dispose of right now. In the following posts, I’ll go over techniques that any developer can use in his day to day code to avoid mocks. These battle tested techniques that I’ve used on different projects in the past. Check the previous post if you’re interested to learn how I came to use them.

This is the second post of a series about how to avoid mocks in automated tests. If you haven’t yet, I recommend you to read my first post to understand the perils of mocks in more details.

The first mock fighting small-scale technique I’ll go over is Immutable Value Objects.

A drawing of a rock written "Immutable Value Object"

What are Immutable Value Objects ?

Behind this weird name is something very simple to understand. Immutable Value Objects :

  • Cannot change their state after construction
  • Only depend on other Immutable Value Objects
  • Don’t change the state of the whole system in any way
  • Don’t do side effects, like inputs and outputs for example

Eric Evans popularized the name in the Domain-Driven Design Blue Book. Immutable Value Objects have existed for decades in functional languages though. We say these objects are immutable (they cannot change) and pure (they cannot do side effects). Here are 2 interesting properties of Value Objects :

  • you can call a method any number of times with no risk of changing anything to the system
  • you’ll always get the same result every time you call the same method on the same object

These by itself, can already be handy when testing.

Cover of Eric Evans's DDD book

How do they prevent mocks ?

That was a bit theoretical, so let’s see how this helps to reduce mocking.

Simpler “init path”

Let’s take it the other way round and see how side effects can lead to mocking. Every test starts with setting the state in which to run the test. Side effects make this complicated, as many objects need to collaborate to set this state up. When this becomes too painful, people start hacking around with mocks. This in turn makes the tests more fragile :

  • We are not testing a “real” situation
  • We need to keep this setup in line with the real code

💡 Intricate state initialization encourage people to use mocks.

Isolates parts of the system

Unfortunately, that is not all the story ! Mutable state also, tricks us into using mocks. As soon as your test deals with mutable state, there is a chance that this state is changed in the ‘real’ system. This means that some bugs might ‘escape’ unit tests and appear in end to end tests or in production. That’s where the mocks strike ! In order to detect this bug in a fast feedback loop, we’re likely to add larger scope tests and use mocks to speed them up …

💡 Mutable state and side effects make unit tests less effective.

Reduces code with side effects

But there’s another reason why Immutable Value Objects help us to avoid mocks. As we’ll try to use them more and more for the previous two reasons, we’ll need to adapt our programming style. As we’ll push more and more code in Immutable Value Objects, the ‘imperative’ part will shrink. This ‘imperative’ part is where side-effect happen. This is the part where mocking out IOs makes sense. To summarize, the more Immutable Value Objects we use, the more isolated the IOs are, and the less mocking we need.

Javascript expert Eric Elliot also wrote about the immutability and mocks here.

Next week

This was the second post in a series about how to prevent mocks in your automated tests. Next post will be an example of using immutable value objects on the FizzBuzz kata.

Careless Mocking Considered Harmful

💡 Mock hell : when excessive use of test mocks makes refactoring extremely slow or difficult.

A few years ago, I managed to get a side project out of mock hell. Since then, I’ve been using what I learned to avoid mocks in all the projects I’ve worked on. This is the start of a series of posts about my mock-avoiding techniques.

A tag "Mocks don't rock !"

Escape from Mock Hell

Between 2010 and 2014, I was working on a side project I called http://mes-courses.fr. Which actually means “my house shopping” in English. I wanted people to be able to do their house shopping in 5 minutes, by using a better UI for online groceries. I was using Ruby, and I had just read Growing Object Oriented Software Guided by Tests. I got a bit too excited with mocking, and was using it way too much.

I’d been practicing Test Driven Development for more than 5 years and I was expecting great results with a language like Ruby. After a few months though, I could feel that something was not going as well as it should. The test initialization code was getting longer and longer, as it included a lot of mock setup. This made the tests more complex and less readable. It also made them unreliable, as it was not rare for all my unit tests to pass while the system was not working. I was taking the habit of running my end to end test more and more often. I was also losing a lot of time maintaining the mock setup code in line with the real classes. Mocks also tricked me into the bad practice of keeping a 1 to 1 mapping between code and test files. That again increased my maintenance burden when moving code from one file to another.

It reached a point where I could not take it anymore. All these problems were pointing at mocks, so I tried to remove them from a test file. Here are the techniques I ended up using to remove them mocks : 

The end result was beyond my hopes, as my problems almost magically disappeared. The code got simpler, I became a lot more confident about my unit tests, and they got easier to maintain. As an illustration, here is an excerpts from the diff of a rails controller test file which went through this mock diet.

A screen capture of a Github diff showing a test file going on a mock diet

What’s the long term risk ?

Basically, excessive mocking arms the maintainability of the tests. Here is what would have happened if I’d done nothing. Tests would have become so painful to maintain that I would have started to ignore or delete them. As coverage would decrease, more and more code would become untested. That’s exactly Michael Feathers’ definition of Legacy Code :

Legacy Code is code without tests. Michael Feathers

To summarize, excessive use of mocks leads to legacy code ! As most of us have learned the hard way, the further a system drifts into legacy, the lower the productivity.

💡 Excessive use of mocks leads to legacy code

Next posts

Others already spoke about the dangers of mocks :

In this series of posts, I’ll go through the details of the different techniques I used to remove mocks. Here is my plan :

  1. Careless Mocking considered Harmful
  2. How Immutable Value Objects fight mocks
  3. Immutable Value Objects vs Mocks : Fizz Buzz
  4. How to use Test Data Builders to avoid mocks and keep your tests clear
  5. One last small scale technique to avoid mocks : Test Matchers
  6. Large scale techniques to avoid mocks
  7. Mocking in special contexts like legacy and dynamically or statically typed languages

Frequently Asked Questions About the 20 Hours of Code Katas

In my previous posts, I explained how to use the 20 hours of Code Katas technique to learn new languages. If you did not read these yet, start by the beginning.

A drawing of FAQ in a lightbulb

To close this series, here are a few tips and suggestions presented as questions and answers.

What if you don’t know TDD yet ?

The few Parisian guys who invented the Coding Dojo wanted to teach and spread TDD ! You should have no problem to use it to learn TDD yourself !

💡 The coding dojo was invented to teach and spread TDD

Pick your favorite language, and schedule a kata plan to practice TDD. Watch one or two videos to see how gurus are doing it. At first, you’ll have to be very careful to stick to baby steps and the red-green-refactor loop. If you need help, check meetup.com for local coding dojos where you’ll find help.

Can I apply this technique to learn something else than a new language ?

As you might have noticed, I used it to refresh my Javascript. I went on to learn different flavors of JS, but also different test libraries. I’ve used in to learn more advanced parts of other languages in the past.

Katas also work well to learn programming techniques like refactoring or DDD. Some nice people shared refactoring katas on the web. To practice DDD, we could repeat katas with the constraint of using Entities and Value Objects only.

You can even use the technique to learn other things like frameworks or tools, but you’ll need to tune it. As I explained before, you need an exercice for deliberate practice and a fast feedback loop. We typically use a Code Katas and TDD for that, but that’s not the only options. Whenever you can find a way to deliberately practice with a fast feedback loop, you’re ready to go ! These days, we should look for docker images with frameworks and tools pre-installed. Going through tutorials without looking at the solutions is deliberate practice. A small live environment can give us fast enough feedback.

💡 Find Deliberate Practice exercices and a fast feedback loop for efficient learning

What if I don’t find any kata ?

Build one yourself ! I’m not joking, building a kata, especially one where you start from scratch is not too difficult. Inspiration comes from anything you happen to do in your daily work. Trim down a programming challenge you had to work, and you might have a kata ! Went to a programming interview ? The question you had to answer might do a nice kata.

"Make things happen" written on a blackboard

Once you’ve created and tested your kata, share it ! There are online kata repositories where you could get a chance to publish it.

One last thing

I just remembered I did not finish my story about my Javascript kata plan. For those wondering, here is the end of story. In the end I did not join this team to do Javascript coaching. After thinking through it for a while, I decided to stop the katas there, and move to something else. I was only 6 hours in, and what was the point to study Javascript not to use it straight away ? The day I’ll need it, I’m likely to have forgotten 80% of it and some of it will be outdated. The knowledge is only another 20 hours away anyway !

That’s what we could call “Just In Time Learning” ! We are drowning in knowledge nowadays. It’s better to have a fast and effective way to learn anything than trying to know everything.

Why 20 Hours of Code Kata Are So Effective for Learning New Languages

In my previous post, I described how I’ve been using 20 hours of Code Katas to learn new languages. If you did not read it yet, have a look at it first. Let’s now look at why it works so well.

In The First 20 Hours Josh Kaufman explains how he learned Ruby in 20 hours. He did not become a Ruby expert, but he was able to build and maintain a static website generator. For my part, I have succeeded to learn a bit of machine learning using the 20 hours technique.

The effectiveness of the 20 hours of Code Katas relies a few key points.

Drawing of "Why ?" mixed up with the inside of a clock

Time-boxing

Time-boxing has 2 main benefits. First, it forces us to stick to what is the most important for us to learn. There is no time to waste slacking around in only 20 hours. Plus it’s a lot easier to focus for 20 hours than over a very long period of time.

There’s a second great thing about time-boxing. The further you go, the less remains to do, and the less likely you are to drop the effort ! We are a lot less likely to abandon when we know we only need a few hours to finish the goal we had set to ourselves.

💡 Time-boxing creates focus

A plan

Again, the plan helps us to focus. We’ll need to choose what gets in a 20 hours plan. Building the plan itself forces us to get a grasp of the learning space. This will help to pick the good stuff to practice.

Routine

Routine is a magic trick to get things done. Once we have a routine in place, we don’t have to think or do extra efforts to find time to learn. The time is already there, we just have to use it !

Deliberate practice

Some exemples from “The first 20 hours” highlight the benefits of deliberate practice. When learning the Colemak keyboard, the author went through typing exercices. When studying the game of Go, he did practices specific situation puzzles. In both cases, deliberate practice made him learn faster. Code katas are typical deliberate practice exercices for programmers.

Picture of a golfer deliberately practicing

Test Driven Development

Coding Dojos are the programmers’ deliberate practice. Coding Dojos traditionally rely on TDD. TDD sets up a fast feedback loop that is key to efficient learning. Think of all the time saved by not having to run and debug a full program every time ! Even dabbling around in the REPL cannot beat running 20 or so test cases every few seconds.

We are already programmers

One last and obvious little detail : we don’t have to learn it all ! When the author learned Ruby in 20 hours, he was starting from scratch ! Unlike us, who already know how to program, but want to extend our knowledge to a few more topics. Most of the times, we don’t need to relearn everything, but to transpose what we know in a new context.

For example, if we already know an object oriented language, learning a new one will be easier. It’s a bit like with foreign languages, the more you know, and the easier it is to learn the next one. In fact, the more languages, frameworks, patterns and paradigms you know, the more the 20 hours code katas will work for you.

💡 The more you know about software, the easier it will be to learn your next programming language.

You might have a look at this post for advices about evergreen concepts to learn.

Next part

This was the second post on this series about the 20 hours of Code Katas technique. The next, and last, post will be compilation of answers to frequently asked questions.

How to Learn a Programming Language in Just 20 Hours

We should not panic when asked to work with a new language. We should be bold enough to answer to job openings requiring technologies we are not used to. In one word, we should not be afraid of new techs. Here is why : by scheduling 20 hours of Code Kata routine sessions, we can get a decent level of mastery on most topics.

A book with built-in clock

How I learned some Haskell

Quite a few years ago, we used to do weekly Coding Dojos at Murex. Arnaud Bailly was among us, and as he is an Haskell fan, we ended up doing quite a lot of katas with Haskell. To my astonishment, after a few sessions, I understood of the fundamentals of the language. Without ever studying it !

💡 I learned a lot of Haskell by just going to Coding Dojos !

Many times afterwards, I learned new languages quickly by practicing them in the Dojo.

How I set out to refresh my javascript

Fast forward to the end of last year. Someone asked me if I could work at coaching a team doing some Javascript. I’ve done some Javascript in the past, but my skills definitely needed a serious update. I decided to use Code Katas to refresh my Javascript. To try to make this even more effective, I decided to mix in a bit of the “First 20 hours” technique.

The cover of the book "The first 20 hours"

I started by defining a plan of 10 sessions of 2 hours long code katas :

  1. Roman NumeralMocha – JS 5
  2. Game of LifeMocha – JS 5
  3. Mars RoverMocha – JS 5
  4. Bowling ScoreMocha – JS 5
  5. Median of a list of lists – MochaES 6
  6. LCD NumbersJasmineES 6
  7. Kata PotterJasmineES 6
  8. T9 – Jasmine – Typescript
  9. Poker handJasmineTypescript
  10. Egg cooker with ReactJasmineTypescript

The plan felt pretty ambitious at the beginning. The first session was a bit hectic as I struggled to find a quick setup I could use to code my kata in. After only 3 sessions though, I could feel I’d already made a lot of progress. I had become confident I would get most of the plan done in 20 hours.

How to start ?

A good thing about the 20 hours technique is that it’s pretty easy to start with ! There’s a catch though ! At the start, it’s puzzling to be on your own with no clear track on how to tackle the topic. Here is the fix : start anyway, stick through, and you’ll work it out 99% of the time.

Here is, in more details, how to use code kata with the 20 hours technique :

  1. Start by setting up a routine. It could be 2 hours at lunch time, 1 hour in the morning or 3 hours at night. Do whatever is best for you. It should be enough to finish in a few days or weeks.
  2. Use the first hours of your 20 hours to setup a code kata plan. It might be very fast if you already have an idea of your learning space. It will take longer if you are a complete newbie. Skim through the literature for the main concepts until you have a plan. Try to keep this phase under 6 hours, otherwise you won’t have any time left for actual learning.
  3. Test Driven Development plays a key role in fast learning ! Next step is to setup a development environment with TDD to use in your Code Kata sessions.
  4. Finally, do you code kata sessions. Time-box them for something less than 2 hours and run a mini-retrospective at the end of every session. Don’t hesitate to adapt your plan if it makes sense.
  5. When you reach 20 hours of learning, stop your sessions. It will be time to think a bit about what you accomplished.

💡 TDD plays a key role in fast learning.

If all went well, you should have learned quite a lot in only 20 hours. At that point, it’s up to you to decide what to do next. You can either decide to dig deeper. In this case, setup a new run of 20 hours of code katas ! It could also be a good time to read a bit of theory about what you just learned. Casting a different light on the topic will make it stick and deepen your understanding. Otherwise, you could stop there and start whatever is on your mind. I don’t recommend continuing on the same topic without rethinking a new plan though. That would kill your focus and be a less efficient use of your time.

To be continued

This was the first post in a series about applying the 20 hours technique and Code Katas to learn new programming languages. Here is the what to expect in the coming posts.

  1. How to learn a programming languages in just 20 hours
  2. Why 20 hours of code kata are so effective for learning new languages
  3. Frequently asked questions about the 20 hours of Code Katas

How to Get the Max Out of Your Team Coding Dojo ?

If you’ve read my previous posts about Team Randori Coding Dojos, you should know why and how to run a successful one.

Did you manage to setup the team Randori coding dojo as a recurring event ? Congratulations ! Your team is on the road to continuous learning and improvement. To close this series of posts, here are battle tested tricks for greatest impact. Let’s boost your teamwork, your production code and a few other things.

Yoda doing the Fizz Buzz kata

Boost your teamwork

I stated before that the team Randori is a perfect occasion to improve your teamwork. By itself, just doing it will already take you a long way towards better collaboration. As instigator of the coding dojo though, you can push the topic faster and further.

Coding and Design Conventions

Whenever you see the opportunity during the dojo, raise design discussions. It’s a good way to share best practices. It often ends up in new coding conventions for the team.

Also don’t forget to use the retrospective. It’s the perfect time to agree on best practices for the dojo and for production code. Push people to dig into what they are mentioning. Ask them if they are willing to adhere to a particular practice. You can use thumb vote to get a quick agreement. Once the team agrees on something, record it somewhere and make sure it is visible to everyone.

Egoless Programming

Egoless Programming makes collaboration a lot easier within a team. In the dojo, demonstrate Egoless Programming yourself. In particular if you already enjoy good peer recognition, adopt a “low attitude”. Don’t hesitate to encourage others to delete your code when they have a better idea. Yourself, don’t hesitate to delete code if it makes sense, but don’t make a fuss about it.

💡 Be a champion of Egoless Programming in Coding Dojo to bring this practice in your team.

Be careful if your workplace is too competitive or if your reputation is not yet strong enough. I’d go slow on this aspect in such situations.

During the dojo, you might notice people who have difficulties with egoless programming. In this case, remind its principes to everyone and that you are here to learn and practice. You can also mention that this is a TDD exercice and that deleting and changing code is the way to go.

The cover of "The psychology of computer programming"

Going further

After enough successful sessions, you’ll want to push further and experiment new things. Absolutely do it ! There’s a lot more to discover about the coding dojos.

Variations

You can try new formats like the Prepared Kata or Randori in Pairs. You can learn a new language by redoing your favorite problems in this language. You can add constraints like “No If”, “Always Compiles” or even exotic things like “No Heap Allocation”. You might also give Emily Bache’s book a read for tons of others ideas.

Production code

If you continue long enough, your team will get particularly good at Randoris. At that point, you might wonder how you could apply this to production code ? It turns you can !

One way I found, which I wrote about in my first post, is to try to fix a local smell or static analysis issue in the code. Get all the team to do a Randori to fix that, discuss the design and conventions, and commit at the end of the session.

Particularly difficult legacy code refactorings are also pretty good candidates for Randoris. 

💡 Given enough eyeballs, all bugs are shallow. Linus’s Law

Once you are there, you might altogether jump into mob programming ! Randoris are by nature, like timeboxed mobs. Replace the Randori rule “Driver decides what to code” with Strong Style pairing (Make the driver code your idea) and that’s it, you are a mob !

Spread the word

One last thing before closing this series on team coding dojos. If the practice is useful to your team, spread it. Chances are that there are other development teams working next to you. Invite members of other teams to your dojo. This will build up their envy for their own team coding dojo. Propose your help to boot their first session !

In the long run, the improved practices of this team might benefit you ! For example, if your teams start collaborating. Or perhaps you’ll join this team some day !

Whatever happens, I wish you a lot of fun in your teams Coding Dojos. Happy Coding !

Coding Dojo Troubleshooting

In my last 2 blog posts, I’ve detailed why and how to start a team Randori Coding Dojo. That’s the easy part. As soon as you start your first dojo, you’ll face trickier issues, especially people issues.

A martial artist with a tool belt

What if my team (or my boss) does not want to ?

Very often some of your team mates won’t see the value of the coding dojo upfront and will prefer to work on other tasks. It can also be your boss, who thinks you should be delivering features instead. Here are a few tricks you can do to make it work.

  • Try to find another time slot. Ask people for their preferred moment. If you can negotiate food sponsorship with your boss, you might get everyone happy. He won’t feel you’re not delivering features, you’ll have a free lunch and you’ll improve your skills.
  • If your boss or colleague doesn’t want to spend 2 full hours on a dojo. Get them to start with smaller problems and a shorter time slot.
  • Your colleagues might have doubts about the value of the dojo. Get them to try it once or a few times before committing to a recurring event.
  • As a general rule of thumb, the more you manage to involve people in the preparation, the more they’ll adhere.
  • If you have 1 or 2 inveterate laggards, do it without them anyway. With time, they’ll understand what they are missing !

💡 If you cannot get people to adopt a new practice, get them to try it once. You’ll be more than halfway there.

Dealing with TDD complaints

A gorilla with a skeptic look

As you’ll start your first Randori, you’ll have some complaints about Test Driven Development. Whether they come from newbies or skeptics, they usually look like :

  • Why do we stick to TDD ? We’d go so much faster if we coded this straight away.
  • We don’t need TDD for such a simple problem.
  • We don’t need such small baby steps on this simple problem.

My answer is more or less always the same. I try to re-frame everyone in the context of a learning exercice of deliberate practice. It could sound something like :

Yes, sure. I know you are professional developers and that you could easily solve this little problem. Keep in mind that we are here to deliberately practice TDD and friends. Solving the problem is only a side effect.

We are going to apply TDD by the book, for the sake of learning. It’s a lot easier to learn to swim in 1 meter of water than in the middle of the sea. Once we’ll master it in the safe dojo environment, you’ll know how to adapt it to your production code.

Please, play by the rules here !

As you can see, I don’t try to convince them. The last thing I want is to get into a pro vs cons of TDD. 95% of the time, this answer is enough to get people over their skepticism and try it for the time of the dojo. Unfortunately, the last 5% might result in a difficult session. There’s no single way to deal with these 5%. You can try to discuss with them in private, or run next session without them.

💡 Reframe the coding dojo as a learning exercice relying on TDD to go beyond skepticism.

How to avoid getting bogged down in details

One last advice, especially for your first sessions. It’s a common rookie mistake to waste 80% of the coding time on error handling. The key is to focus on what you want to learn. You are not writing production code, so don’t hesitate to omit certain aspects. For example, assume that correct arguments are provided to skip error handling. This will save you time, be more fun and increase what you learn.

What’s next ?

This was part 3 of this series on team coding dojo. In the next post, I’ll write how to maximize the benefits we can get out of coding dojos.