Why you shouldn’t Mock in your Tests

Bending the Clean Architecture principles

Anthony Trad
3 min readJun 3, 2022

Introduction

Long story short, I was interviewing for a job and an interesting topic came up. I was asked on how do you do you your tests… I guess like everyone, you want to showcase your potential and how many concepts and techniques do you know and can apply. So I started spilling the tea on this and found out eventually that they don’t use mocking in any of their tests. We are talking from Unit to Integration and E2E testing…

Surprisingly, this interview quickly turned out to be a debate, on when and if we should use Mocking in our Tests so let’s hear it:

Why

When we talk about mocking, we are most probably referring to our database and infrastructure or any external dependency. You have a function that adds a User and sends a confirmation email to the subject.

Sending the email is not part of your business logic and you don’t want to spam your users with emails each time you run your tests. So it might be very tempting to mock and simulate this call as it already happened so that your code can resume. This is mocking an external behavior or dependency…

Another scenario might be to mock your entire database, you want to test your code and your code only. So you don’t care if the database is available or if the query get executed on the ORM level. You just want to make sure that you are executing the right calls with the appropriate params… A smart idea pop into your mind:
Let’s mock our ORM or Repository. That way, we only test our codebase without worrying about the infrastructure !
This is mocking your infrastructure…

Why Not

Let’s dig deeper into this mocking thing for each scenario. To mock, you basically setup a function to return a desired output when called:

AddUser(User user).Returns(1);
User test= new User(…);
int result=AddUser(test);
Console.WriteLine(result); //1

This result will be returned without actually calling the AddUser function. You are just simulating a call with a defined result.

So whenever you mock, you specify the result that you except when the subject is called. This is one of the problems with this approach. Basically you are adding some hypothesis and business logic to setup your tests… It is like creating a virtual world to test your code for a specific scenario.

Let’s say you modify the behavior or the return type of the AddUser(). Your test will start failing and that’s completely normal and ‘good’. But what you will find yourself doing is changing back and forth and modifying the Arrange part of your test to match a specific scenario.

But why are you doing these tests in the first place? You are testing for regressions upon editing or extending your codebase. But if on each modification you will need to fix your test accordingly then you are putting so much effort on a test that you’re not using much. It is the equivalent of setting something that automatically fails upon modification.

I am not saying this doesn’t work. I am just saying that the effort you put in maintaining your test and arranges is very big compared to the benefits you are getting from testing your code in a very static, virtual and defined context.

Next, I know that each layer of tests covers some problems and regressions of its own but sometimes it just does not make sense to mock and it is considered just laziness. It is faster to just mock your entire repository but it is that beneficial?

The Alternative

Nowadays, you can spin up an entire database on a docker image, build and destroy it instantly. You can mount and unmount an entire test environment and clean it up automatically for each test run.

Don’t mock your repo, spin up a isolated database with another configuration and test your entire application. You will accurately replicate a PROD environment and scenarios. Also, your tests will be context free and your setups will no longer contain hardcoded business logic to assert to. Thus, your tests are more maintainable and flexible.

--

--

Anthony Trad
Anthony Trad

Written by Anthony Trad

Senior Software Engineer focused on .NET and the Cloud. Reconsidering major principles and patterns, ideas are my own.

Responses (11)