Write Safer Tests with Bogus
Archived Post: This post is old and is no longer accessible through the main blog page, but we’ve kept it available here for reference.
In this short tutorial, you will learn how to use Bogus, a mocking library created by our developers Adam Pohorecki and Paweł Pierzchała.
What is Bogus?
Bogus is a mocking library similar to RSpec Mocks or Mocha, but unlike those other libraries, it’s designed to make isolated tests less error-prone.
Basic Example
Here’s a method that uses Authenticator to log users in:
class SessionsController
def log_in(user, password)
user = Authenticator.authenticate(user, password)
if user
session[:user_id] = user.id
end
end
end
The easiest way to test this method is to isolate from Authenticator - this way we don’t encode the details of how it works into our tests, making them less likely to change when Authenticator class changes.
This is how a test like that could be written using RSpec Mocks:
it "logs the user in with valid login and password" do
user = User.new(id: 123)
allow(Authenticator).to receive(:authenticate) { user }
sessions_controller.log_in("bob", "secret")
expect(sessions_controller.session[:user_id]).to eq(123)
end
Here’s how the same test could be written using Bogus:
it "logs the user in with valid login and password" do
user = User.new(id: 123)
stub(Authenticator).authenticate(any_args) { user }
sessions_controller.log_in("bob", "secret")
expect(sessions_controller.session[:user_id]).to eq(123)
end
Aside from being a little less verbose, Bogus is not that different from RSpec Mocks in terms of how you write your tests. The difference is in what happens when the Authenticator interface changes.
Safe Stubbing
Let’s say that one day the requirements for the project change, as they always inevitably do, and now our Authenticator class needs to check the user’s IP address before letting her log in.
class Authenticator
# added the ip_address parameter
def self.authenticate(user, password, ip_address)
end
end
If the programmer forgot to update the code in SessionsController, then RSpec Mocks will happily let our original test pass. Bogus, on the other hand, will detect the change in the arity of Authenticator.authenticate and fail with Argument Error when we try to stub that method with 2 arguments.
Not only does Bogus makes sure that the methods you stub exist, but it also stubs them in such a way, that they keep the exact same method signature as it originally had. This feature comes in especially handy combined with Ruby’s keyword arguments.
Conclusion
Tests that use stubs and mocks are great, because they are fast and allow you to test your application one small piece at a time, making it easier to locate and fix issues. However, since they only test one piece of application at a time, they can’t make sure that the pieces of your application fit together and integrate correctly. Bogus is a library that aims to mitigate that problem by adding features like fake stubbing, fake objects and contract tests.
In this article we have seen safe stubbing at work. Stay tuned for future ones where we explain fake objects and contract tests.