Spies, Mocks, Fakes, Dummies, Stubs, what are these?

In many testing scenarios, we want to check if one specific part of our software works. Having dependencies that you need to construct in a way makes testing hard. To lighten the mental load and make testing possible there are test doubles.

Mock is a type of test double. It's not a synonym for test double. This post explains the most relevant.

Please note: the definitions of all of these terms vary quite heavily. The following is what really made most sense to me and you'll always find somebody interpreting it differently.

Dummies

The easiest to create is the dummy. A dummy is used to fulfill the requirement to just have some dependency in place. It does not have to do anything. Its only job is to keep the system free of exceptions when scenario-irrelevant code is called.

class LoginSubscriber
  def initialize(dep)
    @dep = dep
  end
  def notify
    @dep.do_complex_logic
    # actual complex logic would go in here.
  end
end

class DummyLoginSubscriber < LoginSubscriber
  def initialize
    super(nil)
  end
  def notify; end # just do nothing. We don't care about being notified
end

class Login
  def initialize(login_notifier)
    @login_notifier = login_notifier
  end
  def login(username, password)
    @login_notifier.notify
  end
end

Stubs

Stubbing is an easy one, too. You predefine a value, that is returned for a specific method.

Imagine having a class like this:

class LoginUserFeature
  def initialize(hard_to_fake_dependency)
    @hard_to_fake_dependency = hard_to_fake_dependency
  end
  def authentication_successful?(username, password)
    return username == 'example' && @hard_to_fake_dependency.is_valid? # ...
  end
end

class HomeController
  def index_page
  end
end
class LoginUserStub < LoginUserFeature
  def initialize(authentication_successful)
    @authentication_successful = authentication_successful
  end
  def authentication_successful?
    @authentication_successful
  end
end

Spies

Having tests where the state of a specific class itself doesn't matter to the whole flow of the module, you can use spies. Also referred to as testing indirect outputs.

Spies are classes that check if a specific method has been called.

class LoginUserSpy
  def initialize
    @authentication_successful_counts = 0
  end
  def authentication_successful?
    @authentication_successful_counts = @authentication_successful_counts + 1
  end
end

Fakes

Contain actual business logic, with specific rules, that are not suitable for production. Fakes are mostly represented by fake repositories (in-memory repository eg).

# actual database calls
class UserRepository
  def get_user(id)
    # execute sql to get user by id
    User.find(id: id)
  end
end

# in memory database
class UserRepositoryFake
  def initialize
    @users = []
  end
  def get_user(id)
     return @users.find{|user| user.id == id}
  end
end

Mocks

Take care of verifying the indirect output. This was - at least for me - confusing to understand. I will do my best to help you understand with the following example:

class MockUserRepository
  def initialize
    @calls = 0
  end
  def find(username)
    raise "Called more than once" if @calls == 
    @calls = @calls + 1
    {username: username, id: 1}
  end
end

class Login
  def initialize(user_repository)
    @user_repository = user_repository    
  end
  def login(username, password)
    user = @user_repository.find(username)
    user&.password == password
  end
end

# login_spec.rb

describe 'Login' do
  it 'Calls user_repo once' do
    login_flow = Login.new(MockUserRepository.new)
    expect { 
      login_flow.login('alex')
    }.not_to raise_error # if we modify the login logic to ask the user_repository twice for a user, the mock will raise an error
  end
end

Conclusion

It takes some time to understand the differences between the examples. And even after that, these are just opinions from Kent Beck, Martin Fowler, Uncle Bob, blog posts on Medium, etc.

Share this with a colleague and make my day! Cheers!

Read more

https://blog.bitsrc.io/unit-testing-deep-dive-what-are-stubs-mocks-spies-and-dummies-6f7fde21f710

https://nirajrules.wordpress.com/2011/08/27/dummy-vs-stub-vs-spy-vs-fake-vs-mock/

https://martinfowler.com/articles/mocksArentStubs.html
http://xunitpatterns.com/Mock%20Object.html

https://blog.cleancoder.com/uncle-bob/2014/05/14/TheLittleMocker.html