Master testing ResizeObserver in JS. Tackle issues like "resize observer is not defined" and learn to mock it. Gain a solid grasp of testing this essential feature.
The value of basics
As a developer, be prepared for encountering perplexing errors that may seem nonsensical during test execution, and be ready to delve into the task of mocking intricate browser APIs when needed.
That's why having a solid grasp of the fundamentals of the technology you're working with, such as Jest in this context, is important. The seemingly perplexing errors start to make sense once you have a deeper understanding of how the technology operates 💫.
How "Jest" works?
Jest primarily operates in a NodeJS environment and does not use real browser APIs for testing. It's widely used for unit testing, integration testing, and snapshot testing.
Ok, so how I'm able to test my React components?
When testing code that interacts with browser-specific features or APIs, the browser APIs are mocked. Mocking allows you to simulate the behavior of these APIs without actually making real browser calls, making it possible to run tests in a controlled and predictable environment.
When using Jest or similar testing frameworks, it's crucial to be aware of the need to mock browser APIs, especially for newer APIs that might not be automatically mocked. If you forget to mock a specific API, you might encounter errors like "NameOfTheAPI is not defined" during test execution.
The annoying "ResizeObserver is not defined" error
If you've written at least one test involving the ResizeObserver in Jest or another modern web API, you might have come across the perplexing error: "ResizeObserver is not defined" - depends on Jest version you're using.
This occurs because Jest prioritizes delivering maximum test execution speed for developers. It's a straightforward trade-off: Jest significantly speeds up your tests, but they depend on mocked browser APIs rather than the real ones.
Therefore, if you're working with particular versions of the jest or jest-dom libraries, you might encounter errors like the one mentioned. This is because, for certain library versions, the corresponding API may not be mocked yet, especially if it's relatively new.
Let's see it on the GIF:
The error in real situation
In the example provided, we have a Popover component that relies on a ResizeObserver to align the popover position when the element's width or height changes, aiming to maximize the user experience.
Popover component showcase
How to solve "ResizeObserver is not defined" error?
Basically, you have two options:
- update "Jest" package version and pray for having mock added by maintainers,
- add your own mock.
For this particular article, we'll incorporate our own mock since it's a more straightforward and faster approach.
So, let's say you have a ResizeObserver that is used inside useElementSize hook which, is utilized within the Popover component to measure the element size. The implementation of the entire hook is not crucial for our discussion, and the same applies to the Popover component. For our current focus, we'll delve into the ResizeObserver, which is the problematic aspect.
Now, let's say we want to test the two aspects of our hook:
- listening for the resize of the element,
- cleaning listeners when the parent component unmounts.
First of all, we need to mock ResizeObserver that we're using to ensure a smooth testing experience and eliminate the "ResizeObserver is not defined" error.
Now, it's time for testing the resize callback that will be triggered during the resize event.
Last, we need to test if the disconnect function is called when the parent component unmounts. To achieve that, we need the following test. Right now, the additional component is not required. We may test it directly via the renderHook function.
After these steps, tests will be green thanks to the successful mocking of ResizeObserver for these particular cases.
Did you know that there are frameworks for unit and integration testing like Jasmine, which, although slower, eliminates the entire need for mocking browser APIs? This is because, under the hood, the real APIs are used.
The full code and results
I don't want to make this article huge, just wanted to explain the basic concepts. If you're interested in how to fully write a useElementSize hook, feel free to explore the following 🔰 Element size measurement with useElementSize hook article.
Here you've just a small preview of useElementSize implementation and tests.
Conclusions and thoughts
After this article, you know what goes on under the hood in Jest library and the pros and cons of using it in your testing code. While Jest is exceptionally fast, it comes with a small downfall - we need to mock many APIs inside our testing code, and we'll never be entirely sure that the tested code will work precisely as expected inside our browser.
However, we know how to resolve popular errors that may occur during test execution. If you encounter a similar issue with a different browser API than ResizeObserver, you'll be able to easily fix this problem.
If you want to ensure that your code is working correctly in a real browser, you need to use tools like Cypress or Playwright to write E2E tests or utilize Jasmine technology. At the end, it's a topic for other articles.
To learn how to write great tests in React ecosystem check the following React testing spellbook course.