This article is part of Uncommon React, a newsletter focused on helping you become a better React developer by focusing on the less commonly discussed topics, like testing, readability, and more.
Testing is one of the most important activities in software development and React development is no exception to this. Unfortunately, software testing itself is a very overlooked and neglected topic. More often than not, implementing tests is an afterthought and developers are only willing to spend the least amount of time and effort on it. The results are low-quality tests that don't provide any confidence in our software, are meaningless, and are only introducing noise to the codebase.
Proper software testing is a very difficult topic, even more than software development itself. With software development, the beginning and the end are clearly defined. But for software testing, this is not the case. There is no clear path that you can take every time to test your code. It's different every time based on the feature, context, and implementation.
Over the years, I've had my fair share of struggles and difficulties with it. For a very long time, I was writing tests for the sake of writing tests. Just before opening a Merge Request (MR), I would remember that I had to write tests. I would mindlessly copy over the structure from other existing tests and adjust it towards my code. Minimal effort for maximum results, kind of.
The problem was that the resulting tests were far from decent quality, let alone good quality. So often did I think after shipping a feature that it was implemented and tested very well, but so often have bugs been reported in those features. The problem was that I always tested the wrong aspects, tested in the wrong context, or didn't know what to test specifically.
After taking this topic more serious, one of the habits that I've picked up is always going through a testing checklist of topics that I try to cover when testing React components. This article will share that testing checklist, elaborate on the topics, and can help you get through testing your React components easier, create a checklist of your own, and ensure higher quality React code.
The most important things to tests for React components are user interactions. In the end, React development is a form of web development. All the web applications that we create are intended to be used by customers, clients, and users. The most important aspect for all of these user audiences is the interactivity of the application. Without interactivity, the web application is a static website and will hold little value to its users.
When verifying user interactions, it's important to determine what the prerequisites, the specific interaction, and the expected behaviour are. That will be the bread-and-butter layout for most of your tests. First, you set up the test in an environment that can trigger the interaction. Then, you mimic the user interaction. Lastly, you verify that it leads to the expected behaviour.
Every part of this process is crucial in making sure that the tests are useful, meaningful, and reliable. Every scenario will require different answers and different implementations, especially in regards to how to verify the expected behaviour. There are also many ways to implements these kinds of tests, like unit, behaviour, or e2e tests, and different tools, like Enzyme, React Testing Library, Jest, Jasmine, and more. Going over all of these will be a book on its own, but for now, the most important aspect is to start documenting user interactions in tests.
After user interactions, the second most important aspect of any new feature is the requirements from the product manager. In the end, we're writing code and creating software for a particular audience. The party that is responsible for being in contact with that audience, hearing their voice, and managing users' requests is the product manager of your team.
Verifying their requirements are satisfied is equally, if not even more, important than possible user interactions. In the end, the product manager will have (hopefully) thought out every aspect there is to a feature and provide you with a list of requirements. The most straightforward way to translate those into tests is by creating a test for every single requirement.
Ultimately, the requirements are like a checklist for when your feature is completed. Turning those into tests doesn't only verify that they are satisfied, but also lays a foundation of stability, documentation, and confidence for future developers to maintain and work with it.
Most of the time, our focus for testing is on the external users of our code like end-users, clients, or even product managers. Similarly, most of the topics in this article are also geared towards that audience type. But when writing code, there is another type of audience that we should consider, namely other developers.
When performing React development, not all the code will directly affect the application as is presented to the end-user. React development also involves code like internal components and utility functions which are created for other developers to use. But to use them properly, they need to understand them.
There are different ways to document the usage, input, and appropriate results of this internal code, but one of the best ways is using tests. Documenting internal components and utility functions in tests immediately gives an example of how to use them, provides information on what to provide to them, and what the expected behaviour is.
From a client's perspective, especially when your product is released regularly, backwards compatibility is an important aspect. In frontend development, we have a similar thing regarding the dependencies that we use. For the user, being able to upgrade towards newer versions of a dependency or application can be a crucial factor in deciding whether to upgrade, migrate and adopt or not.
Based on personal experience, I either didn't come across this particular topic a lot or just never paid attention to it. Until my most recent job where we're shipping widgets using React regularly. One of the most important topics of discussion is whether a new version contains breaking changes and is backwards compatible.
Having proper tests in place to verify your existing features is a great start and can go a long way in ensuring backwards compatibility. But in frontend development, backwards compatibility isn't always only about features and interactivity. Aspects like SEO, DOM structure, and CSS class names are also part of the conversation. For these, snapshot tests are a great tool to test them with.
So often have I seen the following scenario play out: A bug was reported by support about a feature that we shipped in the past. After a developer picked it up, it turned out that the original developer forgot a specific edge case or one of the requirements. Not a big deal, so the currently responsible developer wrote a fix and shipped the new feature. One week later, a new bug was reported in the same feature. Turned out we caused a regression. We fixed it and shipped it. Another week later, rinse and repeat.
There are a lot of aspects that we can dive into in this scenario regarding testing. But the most important one, for now, is to make sure that the newly added code didn't break the old code in any way. If it did, it's called a regression. From personal experience, regressions are one of the biggest causes of frustration for developers, users, and everyone involved.
To prevent regressions, you first need to have solid tests for the previous code in place. To start with that, it's important to make always add a test that covers when introducing a new feature or fixing a bug. This makes sure that it will not regress in the future.
Testing React components is one of the most important and impactful activities in React development, but unfortunately also one of the most overlooked and neglected ones. During several years of working with React, I've had my fair share of struggles and difficulties with it. After looking into this issue seriously, it became apparent that one of the problems was that I never knew what to test or how to approach it. Contrary to development, I never had a proper routine for testing.
This article shared the checklist that I nowadays go through when testing React components to ensure high-quality tests and code. These include verifying product manager requirements, internal components and utility functions, backwards compatibility features, and making sure regressions don't happen. The specific tooling or tests that you should use will vary and depend on the use case, but the most important aspect is to consider these topics in the first place and create a routine for yourself.