Reliable Mocks with Bogus

Written by: Clemens Helm
5 min read
Stay connected

https://fast.wistia.com/embed/medias/1d771o0uql.jsonphttps://fast.wistia.com/assets/external/E-v1.js

This is the eleventh Testing Tuesday episode. Every week we will share our insights and opinions on the software testing space. Drop by every Tuesday to learn more! Last week we talked about how to design your code in RSpec with stubbing and mocking before you even implement it.


Refactor with Bogus, not Integration tests

In the last few episodes we always backed our RSpec examples with a Cucumber feature that checked if we implemented all behavior. This allowed us to stub behavior of other components in our unit tests, because Cucumber would check if this behavior works anyway.

While this works well for implementing new features it is a pain when you want to refactor your code. You always need to run your whole slow integration test suite to make sure that all your mocked behavior still works.

In this screencast we explore together with James Bond how to solve this problem using a nifty mocking library called Bogus in our RSpec examples.

Up next Testing Tuesday: Our EuRuKo 2013 Recap

Next week there's a Testing Tuesday special edition. I will show you my personal highlights from the European Ruby conference EuRuKo that will take place this Friday and Saturday in Athens, Greece.

Further info:

Transcript

Ahoy and welcome! My name is Clemens the Helm and you’re watching Codeship Testing Tuesday #11.

Last week I showed you how to design your application with stubbing and mocking. Back then we used our Cucumber scenarios to make sure that all behavior we mocked got in fact implemented.

This makes it hard to refactor your code though, because you need to run your slow integration test suite to check if your mocked behavior still works.

This is where Bogus comes in handy.

end intro

This week I feel very honored because James Bond himself ordered an application to manage his missions. One thing he wants to know is when he is ready to start a mission.

Here is the first example of this behavior using RSpec doubles.

describe Mission do
  it "checks protocol for 007’s mission start" do
    meeting_with_m = double outcome: [:top_secret_briefing]
    meeting_with_q = double outcome: [:aston_martin, :laser_Rolex]
    mission = Mission.new meetings: [meeting_with_m, meeting_with_q]
    mission.should be_ready_to_go
  end
end

So after getting the required assets in meetings with M and Q the mission should be ready to go. We've also got a Mission class that's empty so far.

We're mocking the dependency on another class – probably a Meeting class – so we only test the behavior of our Mission. This top-down system design, where classes get implemented before other collaborators exist, is basically very nice, because it makes us independent of the other class' implementation.

But the problem is that we also expect a meeting to have an outcome method. When we implement this code we will probably be driven by an integration test that makes sure that all mocked behavior gets implemented. But when we refactor our code we don't want to run the whole integration test suite to check all our mocks, because that would kill our productivity.

So wouldn't it be nice if our unit tests already verified the methods that we stub?

Bogus is a library that aims to reduce the risks associated with isolated unit testing.

It does not only make sure that our examples work using mocks, it also assures those mocked classes EXIST and have the methods we're stubbing.

Let's run RSpec first to see what error we would get for this example.

RSpec only complains about the constructor of our Mission class that doesn't take any arguments.

Let's try the same with bogus. We need to require 'bogus/rspec' in our spec_helper.rb file and change our example to use bogus fakes:

describe Mission do
  it "checks protocol for 007’s mission start" do
    meeting_with_m = fake :meeting, outcome: [:top_secret_briefing]
    meeting_with_q = fake :meeting, outcome: [:stylish_Aston_Martin, :laser_Rolex]
    mission = Mission.new meetings: [meeting_with_m, meeting_with_q]
    mission.should be_ready_to_go
  end
end

The first thing Bogus tells us when we run RSpec now is that it can't find the Meeting class. So Bogus checks that we don't mock a class that doesn't actually exist. Let's create this class.

The next thing Bogus checks is if there is a method outcome for our Meeting class. Bogus doesn't only check for the classes we mock, but also for the methods we stub.

If we add the method, we finally receive the error message we got before using Bogus. We can fix it by implementing a constructor that accepts one argument. (meeting: meeting) By the way, this is a Ruby 2.0 named argument that makes your code a lot more descriptive. If you haven't heard of them, you should definitely check them out!

But back to the example. We still need a method ready_to_go? to make it work. Aaand ready to go should be true.

Ok, now we've got our example working. Obviously we need more examples for this method to be complete, but let's skip that for now.

So what's the real advantage of using Bogus? When we decide to change our outcome method to be called result at some point, Bogus will immediately complain.

We don't have to run our slow integration test suite to check that our mocked behavior works. This will keep us more productive, agile and – most of all – it will make testing more fun.

This was a very short introduction, but there's a lot more that Bogus has to offer. We'll look into it in more detail in 2 weeks.

I have to go now because I've got to pack my bags for Euruko, the European Ruby conference in Athens. If you go there as well, drop me a line to clemens@codeship.io, I’d be thrilled to meet you there!

If not, check out next week's episode where I'll show you my personal Euruko highlights!

Either way, there's something that I need to tell you: Always stay shipping!

Stay up to date

We'll never share your email address and you can opt out at any time, we promise.

Loading form...
Your ad blocker may be blocking functionality on this page. Please disable for an improved experience.