By Nikolajus Lebedenko, Lead Frontend Engineer at Kilo Health
During the lifecycle of an application, you will start encountering more and more problems while testing the components. Especially if you would delay testing till some later stage.
A perfect example of this is verifying that a bug is fixed in a production application that was never tested.
For demo purposes, let’s imagine that we are trying to test the “Add to cart” form component, which accomplishes the following things:
The dependency scheme for the component would look like this:
Most of the issues while testing components occur because of the dependencies. The “Add to cart” form is not an exception.
The first step to success is to understand the dependencies of a component:
Most of these dependencies will be present in the components which hold business logic. So, what actual problems will we face while testing the “Add to cart” form, and how can we resolve them?
The first problem is that our component requires a few context providers to render:
– `I18nextProvider` for `react-i18next`.
– `Provider` for `react-redux`.
It might sound obvious, but you could not even imagine how much time developers spend trying to understand why a component fails to render.
If you forgot to add a context provider in the test, a component would crash with the error, which would sound like the following – `cannot read property randomProperty of undefined`.
The solution for missing providers is to create a custom render function that lists all the required providers. Later on, replace usage of `render` function from testing library to custom render implementation. This solution is suggested by the Testing Library itself.
In the end, the custom render function would look like this:
const AppWrapper = ({ children }) => {
return (
<I18nextProvider i18n={i18next}>
<StoreProvider store={store}>
{children}
</Store>
</I18nextProvider>
);
};
const customRender = (component, options) => {
render(component, { wrapper: AppWrapper, ...options });
};
Using a custom render function will reduce the amount of boilerplate code, and developers will not need to care much about wrapping components with context providers.
Secondly, our component requires a preloaded state with a predefined product inside. Usually, a state consists of quite complex objects.
For example:
interface Product {
id: string;
sku: string;
name: string;
shortDescription: string;
// ... other properties
}
interface PreloadedState {
product?: Product;
// ... other properties
}
Even though the “Add to cart” form needs only one property from the `Product` interface, we will need to pass an object that matches the `Product` interface.
Imagine if the component depended on several complex objects – it would be a nightmare, and you would spend tons of time defining the initial state.
The solution for complex initial state issues is to implement a builder function that would generate an entity of your need.
A product builder example:
type ProductBuilder = (product: Partial<Product> = {}) => Product;
const aProduct: ProductBuilder = (product) => ({
id: randomId(),
sku: randomSku(),
name: randomProductName(),
shortDescription: randomDescription(),
...product,
});
const randomProduct = aProduct();
const productWithSpecificName = aProduct({ name: 'My product' });
const store = createStore(rootReducer, { product: aProduct() });
In builder functions, you may use libraries that would generate random properties such as ID, SKU, product name, etc. Using builder functions would allow it to easily generate entities for the components with a possibility to override certain properties.
The last problem you will encounter while testing the “Add to cart” form is depending on Axios and GTM. To be precise, that component will make real HTTP requests to the server and try to access the `window.dataLayer` object, which will be missing.
To resolve the last issue, we will need to do some refactoring and remove the direct dependency on problematic libraries.
After refactoring, the scheme would look like this:
This way, the “Add to cart” form depends on implementations that match HTTP client and analytics service interfaces. For this reason, we are pretty flexible in creating test kit implementation for our dependencies and forwarding them using context providers. It will allow us to avoid HTTP requests or any environment-specific code.
Also, providing a possibility to verify that integration between a component and service works as expected.
Even using a simple example of the “Add to cart” form, we have already faced some issues while writing component tests.
Ask, research, support. These three words sum up my role — or maybe just the nerd face emoji. Currently, I am working as a Scientific Research Lead at Kilo Health. But before that, I’ve always been on a similar path,…
My experience comes from aviation, but now I lead a wellness and HealthTech company. Business lessons are universally similar, and continuous success requires adaptation and growth. In this article, I want to share the most important points — or perhaps…
As we’re entering an exciting new chapter of business growth and leadership, it’s the perfect time to catch up on the latest changes in our team and where we’re headed next. Dive in and get to know our new CEO…
I’m Matas, and when asked, I refer to myself as an intern — always learning. However, research, strategy, business development, and idea generation are the cornerstones of my work. I want to pull back the curtain and give you an…
I’m Deimante, currently Head of Marketing at Kilo Health, and a big lover of this company. My journey to being hired at Kilo has been quite the ride. How it all started? I underwent interviews with 11 different people and…
Ever dreamt of taking the lead, even if the path isn’t crystal clear? Or to have someone believe in you and offer you a chance to figure out whether you would thrive in a startup environment? Speaking of which, Kilo…
A whole decade has raced by in the blink of an eye for us at Kilo Health, and what better way to celebrate than to reflect on the milestones and lessons over the years? Do you know where we started?…
We, the co-founders, are just ordinary individuals with grand ambitions. There are times when we work twice as long and intensely as others, yet we’re equipped with the same amount of daytime, energy, and capacity. However, as leaders in the…
There’s no enchanting tale behind how I became a part of Kilo Health. In truth, some of us regular folks simply have regular journeys, and that’s perfectly fine. What counts is that today, I hold a successful product in my…
Reflecting on your achievements from the previous year is advantageous. That’s exactly what we did, proudly demonstrating our boundless aspirations through an impressive 84% growth and 213 million euros. So let’s put our hands in the air and celebrate together,…
I joined Kilo Health back in 2019, and I can prove that when people’s values and mindsets align, great things can be accomplished, even if you don’t have a plan. BoomeranGO!, the first and only product for children provided by…
Lighting, sound, set, and actors are essential components of a film studio, but they are not the sole factors that define its success. Consistent creativity, appreciation of talent, and adaptability to market trends are a few of the things that…
A Spinter Research survey shows 29% of young Lithuanians aged 18-25 use AI tools like ChatGPT. Adoption is highest among executives, students, and small business owners, with usage nearly equal between men and women. The Girls in Tech Day initiative…
Kilo Health’s new CEO, Zygimantas Surintas, joined the company about six months ago and sees it as a “diamond in the rough” with great potential but in need of strategic changes following rapid, self-funded growth. Surintas emphasizes the importance of…