Benito Serna Tips and tools for Ruby on Rails developers

Use mocks to design your dependencies when using TDD with a use case approach

November 7, 2018

Have you been in that situation when you are “mocking” something and you feel that you are not testing anything?

I have already told you that organizing your code in use cases can help you to start your projects using TDD, gave you a tip on how you can organize your app in use cases, and also share with you a guide to help you structure your tests when using TDD with this use case approach.

But then when you want to start writing your tests, that question arrives… What should I mock?

And maybe also… How should you do it?

Do you have this questions?

To expand a little more, let’s use an example…

Let’s say, that you are working in a web app about documents… And your first task is to create a page that list all documents metadata, stored somewhere (maybe a database table).

You are going to start doing TDD for the function Docs.all_documents, and you are thinking in using ActiveRecord, with something like Document.all inside that function.

Then maybe you are start thinking…

Should I write a mock to substitute active record?
If I do it… then what am I testing?

Have you had this feeling?

Now, let’s use another example…

Imagine that you are using rails and your use case needs to send an email and you don’t want to use the real implementation in you tests… So, you try to write a mock for the rails mailer that is expected to be used like this…

UserMailer.with(user: user).new_investment_email.deliver_later

Don’t you think that this will be hard and can make your tests and your use case too rigid?

Try to use your mocks to design your dependencies…

Here you are seeing your dependencies as something concrete… What if instead you try to depend on custom abstractions?

If you are working in the use case function Docs.all_documents and you want to mock the database… Don’t write a mock for your actual database, or for ActiveRecord, or whatever you think you are going to use… Try to write a mock for the thing that will bring you the data that you need.

Try write mocks to define or design the object that your use case needs and not for the tools that you already have… Well, really it will be a combination of both, but the use case under test should be the thing that really drives your decision.

In this way for example…

If you are using rails and your use case needs to send an email, you don’t need to write a mock that implements the interface of ActiveMailer… Instead try to design the interface that will help your use case to be simpler.

For example, instead of expecting something like…

module Investments
  def self.register_investment(attrs, config)
    # ....
    mailer = config.fetch(:mailer)
    mailer.with(user: user).new_investment_email.deliver_later
  end
end

You could do expect something like…

module Investments
  def self.register_investment(attrs, config)
    # ....
    mailer = config.fetch(:mailer)
    mailer.send_new_investment_email(user)
  end
end

Then you can write an adapter, that will call the rails mailer…

class NewInvestmentMailer # or the name that you want
  def self.send_new_investment_email(user)
    UserMailer.with(user: user).new_investment_email.deliver_later
  end
end

config = {
  #...
  mailer: NewInvestmentMailer
}

Investments.register_investment(attrs, config)

If one day a rails changes the DSL for the mailer your use case will not need to be changed… Because it has not changed.

Don’t you think that is nice to have control over your dependencies?
… And that using mocks isn’t that hard and that bad?

Related articles

Weekly tips and tools for Ruby on Rails developers

I send an email each week, trying to share knowledge and fixes to common problems and struggles for ruby on rails developers, like How to fetch the latest-N-of-each record or How to test that an specific mail was sent or a Capybara cheatsheet. You can see more examples on Most recent posts or All post by topic.