When writing tests for services, you may sometimes want to use mock objects instead of real objects. In case you're using ActiveRecord and real objects, your tests may hit the database and slow down your suite. The latest release of the rspec-mocks library bundled with RSpec 3 includes at least three different ways to implement a mock object.

Let's discuss some of the differences between a spy , a double and an instance_double . First, the spy :

[ 1 ] pry ( main ) > require 'rspec/mocks/standalone' => true [ 2 ] pry ( main ) > user_spy = spy ( User ) => #<Double User> [ 3 ] pry ( main ) > spy . whatever_method => #<Double (anonymous)>

The spy accepts any method calls and always returns itself unless specified. If you need the mock object to raise if it receives an unexpected method call, you can use a double instead:

[ 4 ] pry ( main ) > user_double = double ( User ) => #<Double User> [ 5 ] pry ( main ) > user_double . whatever_method RSpec :: Mocks :: MockExpectationError : #<Double User> received unexpected message :whatever_method with (no args) from /Users/m auro - oto /. rvm / gems / ruby - 2 . 2 . 1 @carbide / gems / rspec - support - 3 . 5 . 0 / lib / rspec / support . rb : 87 :in block in < module : Support >

If you haven't specified that the double can receive a given method, in the case above whatever_method , it'll raise an exception. You can explicitly tell the double that it can receive such a method and its return value like this:

[ 6 ] pry ( main ) > user_double = double ( User , whatever_method : nil ) => #<Double User> [ 7 ] pry ( main ) > user_double . whatever_method => nil [ 8 ] pry ( main ) > user_double . some_method RSpec :: Mocks :: MockExpectationError : #<Double User> received unexpected message :some_method with (no args) from /Users/m auro - oto /. rvm / gems / ruby - 2 . 2 . 1 @carbide / gems / rspec - support - 3 . 5 . 0 / lib / rspec / support . rb : 87 :in block in < module : Support >

This way, whatever_method can be called and nil will be returned, which is the return value we specified. Any other method calls will fail if we haven't specified them (e.g. some_method ).

If we want to have even more control over what happens with our mock object, and disallow arbitrary method creation like whatever_method or some_method , we can use a verifying double, which exists since RSpec 3 as instance_double :

[ 9 ] pry ( main ) > user_verifiable = instance_double ( User , whatever_method : nil ) RSpec :: Mocks :: MockExpectationError : the User class does not implement the instance method : whatever_method from /Users/m auro - oto /. rvm / gems / ruby - 2 . 2 . 1 @carbide / gems / rspec - support - 3 . 5 . 0 / lib / rspec / support . rb : 87 :in block in < module : Support >

If we try to declare a method which is not implemented by the class of the mocked instance, it will raise an exception. If we decide to use mock objects in our tests, instance_doubles provides us with a bit more confidence in our tests than if we were using spies or regular doubles.

The performance of instance_double is slightly worse than double or spy because verifying doubles are more complex. The difference between using a verifying double and a real object is quite significant:

Benchmark.ips do |bm| bm.report("spy") { spy(User, id: 1) } bm.report("double") { double(User, id: 1) } bm.report("verifying double") { instance_double(User, id: 1) } bm.report("actual object") { User.new(id: 1) } bm.report("via factorygirl") { FactoryGirl.build(:user, id: 1) } bm.compare! end Warming up -------------------------------------- spy 402.000 i/100ms double 572.000 i/100ms verifying double 424.000 i/100ms actual object 153.000 i/100ms via factorygirl 92.000 i/100ms Calculating ------------------------------------- spy 29.174k (±31.6%) i/s - 55.878k in 5.575866s double 21.567k (±37.5%) i/s - 35.464k in 5.599092s verifying double 9.418k (±36.4%) i/s - 10.600k in 5.031771s actual object 1.226k (±37.3%) i/s - 3.366k in 6.897566s via factorygirl 1.037k (±27.4%) i/s - 2.300k in 7.289933s Comparison: spy: 29174.4 i/s double: 21567.0 i/s - same-ish: difference falls within error verifying double: 9417.5 i/s - 3.10x slower actual object: 1226.1 i/s - 23.79x slower via factorygirl: 1036.7 i/s - 28.14x slower

If you are testing a service and don't care about testing ActiveRecord callbacks or database interactions, you will likely be better off using a double. If you are already using spies or doubles, you may want to use a verifying double instead. I think the slight performance hit of verifying the object's implementation is worth it.