Mixing it up with Mocktail
Tags: programming, ruby, testing
Published: 2022-08-31 15:40:59

---
title: Mixing it up with Mocktail
published: true
date: 2021-11-12 00:00:00 UTC
tags: programming,ruby,testing
canonical_url: http://calebhearth.com/mix-with-mocktail
---

[Mocktail](https://github.com/testdouble/mocktail) is a new testing library from the lovely folks at Test Double. It seeks to provide a more modern, less intrusive, and friendlier interface for using [test doubles](https://martinfowler.com/bliki/TestDouble.html "TestDouble by Martin Fowler") (not to be confused with the authors) than prior art such as rspec-mocks, Mocha, rr, and other options currently available.

In the words of its [author](https://github.com/searls "Justin Searls"), the “huge runaway headline is that you can inject a fake into production code in one line and without bending over backwards to use dependency injection or mess with mocking `#new` manually”.

In this article, we’re going to work through a TDD bartender class and because it’s on theme and a Friday, we’re going to have it mix us up a nice [Vieux Carré](https://www.seriouseats.com/vieux-carre-recipe "Vieux Carré Recipe"). We’ll start really simply with an empty test case.

```ruby
require 'minitest/autorun'
require 'mocktail'

class VieuxCarreTest < Minitest::Test
  include Mocktail::DSL

  def test_mixes_up_a_lovely_drink
  end
end
```

Your app probably has a more complex setup, but note that in our case, our Gemfile is really simple:

```ruby
source "https://rubygems.org"

gem "minitest"
gem "mocktail"
```

Our bartender is going to use a mixing glass, and add the ingredients for our drink:

```ruby
def test_mixes_up_a_lovely_drink
  glass = Mocktail.of_next(MixingGlass)
  stubs { glass.stir }.with { :yummy_drink }

  assert_equal Bartender.new.vieux_carre, :yummy_drink

  verify { glass.add(:rye, amt: 0.75, measure: :oz) }
  verify { glass.add(:cognac, amt: 0.75, mesaure: :oz) }
  verify { glass.add(:sweet_vermouth, amt: 0.75, measure: :oz) }
  verify { glass.add(:benedictine, amt: 0.5, measure: :tsp) }
  verify { glass.add(:angostura, amt: 1, measure: :dash) }
  verify { glass.add(:peychauds, amt: 1, measure: :dash) }
end

class MixingGlass
  def stir; end
end

class Bartender
  def vieux_carre
    glass = MixingGlass.new
    glass.stir
  end
end
```

That first line is the magic Justin told us about earlier: the bartender is going to grab the mixing glass herself–we don’t need to hand it to her when we sit down and order. Because we’re not planning to use dependency injection to pass in the mixing glass, the line `Mocktail.of_next(MixingGlass)` tells Mocktail to return a test double for the next `Mocktail.new` call and gives us a reference to what it’ll return so we can make expectations on that.

The other really helpful thing that’ll happen when we run this test is that Mocktail will give us a great error message about the missing `#add` method:

```
  1) Error:
VieuxCarreTest#test_mixes_up_a_lovely_drink:
NoMethodError: No method `MixingGlass#add' exists for call:

  add(:rye, amt: 0.75, measure: :oz)

Need to define the method? Here's a sample definition:

  def add(rye, amt:, measure:)
  end

```

We could copy that right into the class if we wanted to, and if we’d used `:ingredient` instead of `:rye` the signature would be perfect–good to keep in mind for next time.

Now that we’ve got the method signature in place, we can run the test again to see that of course we’re not actually calling `add` anywhere. Let’s go ahead and write out what we’d like our API to look like and code up the recipe steps in Bartender. We’ll intentionally make some mistakes in the recipe to see what Mocktail tells us:

```ruby
class Bartender
  def vieux_carre
    glass = MixingGlass.new
    glass.add(:rye, amt: 0.75, measure: :oz)
    glass.add(:brandy, amt: 0.75, measure: :oz)
    glass.add(:sweet_vermouth, amt: 1, measure: :oz)
    glass.add(:benedictine, amt: 0.5, measure: :oz)
    glass.add(:angostura, amt: 2, measure: :dashes)
    glass.add(:peychauds, amt: 1, measure: :dash)
    glass.stir
  end
end
```

Running the test again, we get another useful error message, albeit this time from Ruby and not Mocktail. We misspelled one of the keyword arguments:

```
  1) Error:
VieuxCarreTest#test_mixes_up_a_lovely_drink:
ArgumentError: missing keyword: :measure [Mocktail call: `add(:cognac, amt: 0.75, mesaure: :oz)']

```

Fun fact: vim has a spellcheck function so if I place the cursor over `mesaure` and type `z=`, it will populate a list of suggestions and I can pick the first by typing `1` and it will fix my mistake.

When we re-run our tests, we can see another useful error message from Mocktail complaining that we’ve missed a step and showing us all of the calls that we _did_ make to `glass.add`:

```
  1) Error:
VieuxCarreTest#test_mixes_up_a_lovely_drink:
Mocktail::VerificationError: Expected mocktail of `MixingGlass#add' to be called like:

  add(:cognac, amt: 0.75, measure: :oz)

It was called differently 6 times:

  add(:rye, amt: 0.75, measure: :oz)

  add(:brandy, amt: 0.75, measure: :oz)

  add(:sweet_vermouth, amt: 1, measure: :oz)

  add(:benedictine, amt: 0.5, measure: :oz)

  add(:angostura, amt: 2, measure: :dashes)

  add(:peychauds, amt: 1, measure: :dash)
```

We can go ahead and correct our mistake, swapping our brandy for the correct cognac (sue me, my home bar doesn’t always have both of these). When we go again, Mocktail catches that I like my drinks on the sweet side and complains about the next mistake:

```
  1) Error:
VieuxCarreTest#test_mixes_up_a_lovely_drink:
Mocktail::VerificationError: Expected mocktail of `MixingGlass#add' to be called like:

  add(:sweet_vermouth, amt: 0.75, measure: :oz)

It was called differently 6 times:

  add(:rye, amt: 0.75, measure: :oz)

  add(:cognac, amt: 0.75, measure: :oz)

  add(:sweet_vermouth, amt: 1, measure: :oz)

  add(:benedictine, amt: 0.5, measure: :oz)

  add(:angostura, amt: 2, measure: :dashes)

  add(:peychauds, amt: 1, measure: :dash)
```

We’ll go ahead and undo all of our mistakes now that we’re sure Mocktail will catch us. Whatever happened to being creative with drinks?

```ruby
class Bartender
  def vieux_carre
    glass = MixingGlass.new
    glass.add(:rye, amt: 0.75, measure: :oz)
    glass.add(:cognac, amt: 0.75, measure: :oz)
    glass.add(:sweet_vermouth, amt: 0.75, measure: :oz)
    glass.add(:benedictine, amt: 0.5, measure: :tsp)
    glass.add(:angostura, amt: 1, measure: :dash)
    glass.add(:peychauds, amt: 1, measure: :dash)
    glass.stir
  end
end

```

And indeed, our drink is yummy.

The great error messages, the fact that it provides really easy `Class.new` interception, and the stupidly obvious `stub {... }.with {...}` API are all solid reasons to give Mocktail a try in your projects. The argument validation we saw part of is also great, and we didn’t even go over that the stub/verify APIs also ensure that your arity is correct so you can’t accidentally stub out a call, change your method signature, and not catch that you’ve broken things.

We didn’t look into examples, but Mocktail also provides some really powerful argument matching (`verify(times: 6) { glass.add(is_a(Symbol), amt: numeric, measure: any }`), singleton replacement for doubles of classes allowing for stubs and verifys while maintaining thread saftey, and a powerful argument verification API in the form of [`Mocktail.captor`](https://github.com/testdouble/mocktail#mocktailcaptor).

Mocktail, unlike the most popular test double library rspec-mocks, isn’t tied to a single framework, so it meets you where you are regarding testing frameworks. It doesn’t monkey patch any of your objects, so you don’t need to worry about namespace collisions or other issues that can cause.

So, take a shot of [Mocktail](https://github.com/testdouble/mocktail). I think you’ll like what you get.