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.
- 1. Basics
10 minutes
Software testing
2 m
Grouping the tests
1 minute
The usage of describe and it
2 m
The best practices for naming tests
2 m
Navigating the different types of software tests
3 m
- 2. Mastering unit testing
38 minutes
Project and tests setup
3 m
Unit tests review
4 m
React component testing
5 m
Snapshot testing in React
4 m
Understanding stubs in testing
3 m
Understanding mocks in testing
5 m
Creating testing fixtures
4 m
Using spies in React and Typescript
3 m
Mocking environment variables
3 m
Using dependency injection pattern to improve fixtures
4 m
- 3. Mastering integration testing
12 minutes
Understanding the integration tests
4 m
Using MSW library to remove implementation details from tests
4 m
Creating fixture for MSW to reduce boilerplate and setup
4 m
- 4. Mastering e2e tests
8 minutes
Comments
Add your honest opinion about this article and help us improve the content.