Migrating from Mocha to rspec-mocks
At Financeit, our main app uses RSpec for the test suite, but didn’t use rspec-mocks. Instead, it used Mocha (not to be confused with the JavaScript library of the same name) for stubbing and mocking.
We recently decided to switch to use rspec-mocks. Some reasons for this were:
- We were already using rspec-mocks in other projects.
- There are more reference and training materials for rspec-mocks (books, screencasts, blog posts, etc).
- New developers joining the team were typically already familiar with rspec-mocks.
We didn’t have any major problems with Mocha itself – it’s well-designed, has good documentation, and a responsive maintainer. The main reason was to have a simple and consistent stack.
Approach
As our test suite is large, we knew that an incremental approach would be needed.
We made use of the rspec-multi-mock gem to allow Mocha and rspec-mocks to both be used at the same time.
We then introduced a policy that new specs should be written using rspec-mocks rather than Mocha. Our code review process helped to remind developers of this.
Next, we began the process of converting the test suite. Most Mocha has a direct mapping to rspec-mocks. I created this ‘cheatsheet’ for easy reference:
Mocha | rspec-mocks |
---|---|
stub(a: 1) |
double(a: 1) |
mock(a: 1) |
double(a: 1) |
foo.stubs(:a).returns(1) |
allow(foo).to receive(:a).and_return(1) |
foo.expects(:a).returns(1) |
expect(foo).to receive(:a).and_return(1) |
Foo.any_instance.stubs(:a).returns(1) |
allow_any_instance_of(Foo).to receive(:a).and_return(1) |
The most obvious way to make these conversions would be using regular expressions. And although this handles around 80% of conversions, the remainder would need to manually reviewed and edited, or would need very complex regular expressions.
As a general rule when changing code with automatic tools, it’s better to modify the AST (Abstract Syntax Tree) than to manipulate strings.
Ruby already has a powerful tools for this: RuboCop, which makes use of the parser gem, has an auto-correct feature to change code to follow a preferred style.
RuboCop Custom Cops
At Financeit, we have a concept of ‘spike weeks’ where developers get to work on something they have a personal interest in. This gave me an opportunity to explore this area.
RuboCop can be extended with custom cops – the documentation is somewhat limited, so it took some time to get the hang of, but after that I found it to be a very effective approach.
I worked through the app in stages, beginning with spec/controllers
, then
spec/models
, spec/services
, etc.
The test suite itself helped to verify that the conversions were correct, since bad conversions should typically result in a syntax error, or a failing test.
I ran into a few situations where the conversion didn’t work because of some strange approaches in the test, so took the opportunity to re-write the test.
We’ve now converted around 95% of the test suite. We hope to soon reach 100%, when we can then remove the rspec-multi-mock and Mocha gems.
I’ve published the RuboCop custom cops as the mocha_to_rspec gem.