Article thumbnail

Understanding mocks in testing

5m
frontend
testing
quality

Intro

What mocks are? If you are curious you need to read this article. We'll check different kinds of mocks and we'll use them in practice.

Prelude

During the earlier lesson, we learned the stubs and realized what they are used for. Now is the time for mocks, which are used very often during unit testing.

Mocks definition

Mocks are used to replace the implementation of a specific module. Their main objective is to provide:

  • Isolation - you can define the expected behavior of the mock and specify the responses
  • Control - they can simulate different scenarios and edge cases that may be challenging to reproduce with real dependencies
  • Simplicity - they eliminate the need for complex setups or configurations that may be required for using real dependencies
  • Performance - mocks can be faster and more efficient than using real dependencies
  • Testing unavailable or unreliable modules - by mocking module which is broken you can test the behavior of your code

Why are mocks so important?

Think of a situation where you make a query to a mailing service in the code that you are testing. If you don't mock the API of that service then you're making real queries.

It would cost you a bit 💰.

Why do we need to use mocks?

Let's say we have the following function that is designed to build a path for specific article:

Loading

What catches the eye immediately is the use of the path module and the globally available process variable.

The path module is not a part of our application, but an external library - we assume that it works properly, we don't want to test it.

The process variable is an object created by the NodeJS runtime environment. It has properties and methods. Also, we have to assume that it works correctly and we don't want to test it.

How should we test it? We need to mock both and simulate the behavior of the cmd() and join() methods. We know how they work so it won't be a problem.

Let's mock

This is how the join() and cwd() methods work:

Loading

Now all we need to do is create a test in which we substitute their implementation and check whether our own function works as expected.

Loading

In the first mock we replace the implementation of the path module and the join method. Thanks to the jest.fn() we will be able to change the implementation in any other test as needed.

Loading

In the second one, we used defineProperty to override a globally available process object property.

Loading

It is very important to assign a starting value to global objects after testing. If we don't do this, we expose ourselves to side effects and the fact that tests in this file can affect other tests - which is very bad!

Note the use of the object descriptor (writeable: true), which allowed us to restore to the initial value later. Without this change, it is not possible to overwrite the cwd property implementation.

Summary

Bravo! Now you know how important mocks are and how to create them for modules, methods and global objects. You have to admit that their use is much wider than in the case of stubs.

If you enjoyed it, be sure to visit us on Linkedin where we regularly upload content from programming.

I create content regularly!

I hope you found my post interesting. If so, maybe you'll take a peek at my LinkedIn, where I publish posts daily.

Comments

Add your honest opinion about this article and help us improve the content.

created: 10-04-2023
updated: 10-05-2023