Leverage MSW library to eliminate implementation details leaks in integration tests. Enhance testing robustness for more effective development workflow. (134)
Good to know before reading
If you are not familiar with the integration testing core concepts, feel free to check Understanding the integration tests article before reading this post.
In the above mentioned article, we covered the basics of integration testing. The conclusion at the end was: "How we can remove implementation details leak from our tests?". I proposed a library that is called msw - mock service worker, so right now we will integrate our test environment with this technology and we'll remove implementation details from our test suites.
What the hell is a mock service worker library?
This technology allows us to run Node process in the background that intercepts HTTP requests and allows us to override responses for specific endpoints. We can add custom logic, static JSON responses, and whatever we want. It can be understood as a middleware between your application/testing environment and HTTP layer.
The most popular use cases are:
- mocking responses for integration tests,
- mocking responses for backend endpoints during local development, it's a situation when backend devs still work on some endpoint, but it's not finished yet.
If you know the service workers concept from PWA, the msw is something similar but designed for different use case - mocking stuff for development purposes.
The configuration of MSW
It's really simple, first of all, we need to install the package msw. Open the terminal and run the following command: npm i --save-dev msw.
Keep notice that the package should be installed as developer dependency. It's because we'll use it only for development purposes!
I'll skip the demonstration of how it works in a real, opened application. This lesson is about testing. So, let's dive into configuration for testing.
That's all what is needed for configuration or msw, other code needs to be applied in test suites.
Using MSW in tests
I will say it at the beginning - the code for using msw in tests will be mostly repeated - it requires some boilerplate. It's the best case for using the fixture pattern.
If you are not familiar with fixtures I recommend to read the following Creating testing fixtures article.
Now, let's create a fixture that will allow our testing code to set and get what will be needed for our test logic.
Now when we want to use msw interception in test suites, we'll need to import the above created fixture and we'll use it in the following way:
We have overridden a default mock from server.ts file. Next, we added some boilerplate connected with starting, mocking, and closing the Node process - it will be required in every test file. Lastly, if you have default mocks in server setup file (the one that we created at the beginning), I recommend to be careful - your test code may be harder to read, but u2y.
Refactoring implementation of fragile test suites
At the beginning let's display our test code that we'll refactor.
It's time to remove ugly implementation details with msw.
A little summary:
- we removed service layer mocks,
- no more implementation details,
- no more need for ugly "jest.requireActual" usage,
- if the implementation of service layer change - we don't care anymore, test will work correctly and will not return "false negatives".
Concerns and results of our refactor
That was a good refactor... We made our tests less fragile to implementation change in our service layer - HTTP logic.
However, we still have some boilerplate that needs to be added and repeated across different tests... Good news! It's fixable and we'll cover it in the next article.
- 1. Basics
Grouping the tests
The usage of describe and it
The best practices for naming tests
Navigating the different types of software tests
- 2. Mastering unit testing
Project and tests setup
Unit tests review
React component testing
Snapshot testing in React
Understanding stubs in testing
Understanding mocks in testing
Creating testing fixtures
Using spies in React and Typescript
Mocking environment variables
Using dependency injection pattern to improve fixtures
- 3. Mastering integration testing
Understanding the integration tests
Using MSW library to remove implementation details from tests
Creating fixture for MSW to reduce boilerplate and setup
- 4. Mastering e2e tests