top of page

Reducing Cross - Team Production Incidents by Using Testkits

Introduction


About a year ago I started working at Wix, where a big part of our daily tasks is cross-team integrations. Various teams at Wix are exporting UI components that my team, Wix-Bookings, is integrating into our view. Each integration functionality is covered by integration tests.


When writing component tests, you can mock the 3rd party component, but in this article, we will focus on the benefits of using the component’s testkit for better and simpler creation of integration tests.




Why do you need integration tests?

The whole idea of tests is to prevent future production incidents in case the code changes in an unexpected way. That’s why we try to mock as little as we can in integration tests. This way we can catch breaking changes in our integration test and hold off deploying a new version to production until the issue is resolved.

For example, a fellow team exports a modal with a “Save” button and we decide to mock the whole modal. But then a week later they decide to delete the “Save” button and forget to let us know - we won’t catch the mistake while testing and will get it in production. This is exactly where a testkit can come to the rescue.



What is a testkit?


A testkit is a class/function that gathers all the testing logic and simplifies the test suite as much as possible, leaving the test suite with just the business testing logic that’s as close as possible to plain English and agnostic to testing libraries/frameworks like enzyme or @testing-library/react.


A testkit should allow its consumers to perform various operations while hiding the implementation completely. For example, DOM interactions - allow the consumer to click a button without them needing to figure out the component’s class/id/attribute, or mock API calls of your exported component and, if needed, allow your consumers to also mock your API calls.



Testkit Example

Before we start, please note that you can find the full example in this github repo, and this sandbox.

Wix-Bookings is here to help users integrate booking tools into their Wix websites. So let’s imagine we have a new integration with the Coupons team that will expose a Create New Coupon modal. Wix-Bookings will create the button that is responsible for opening the modal.




Create a new Coupon


and here’s the API for the Coupon’s modal:


Of course those aren’t a real modal and a real button, they’re created only for demonstration purposes.

When a user clicks the button, the modal will open, and when they click on the save button, a coupon will be saved into the user’s coupons area so that later they will be able to send it to their users.


So let's create a potential coupon Modal, test suite & testkit, and later integrate the testkit in Wix-Bookings’ test suite as well.



Let’s start with a potential Coupon test suite and a potential test suite and see the testkit implementation right after that:



Remember, this is just a basic example, therefore we only have 1 test.

Here’s the testkit implementation:



Tip: Write a meaningful README file for your testkit

There are some people who like the `beforeAndAfter` pattern, while others argue that encapsulating initialisation and reset operations is inferior to controlling these inside the spec suite separately. Please comment below with your preference. If you eventually decide to use this pattern, please don’t forget to add it to the testkit’s Readme file.

Another pattern we use which is out of the scope of the article but worth mentioning is the driver pattern. A driver will contain DOM interactions like “clickSaveButton”, but since we’re not going to be focusing on it in this article, we’ll tuck DOM interactions under our testkit.


Bookings’ button implementation:



Bookings’ test suite:



As you can see above, we are now able to easily create an integration test with a modal, created by the Coupon’s team, without knowing anything about the Modal implementation. Also we don’t need to reverse engineer the modal save button id, for example.

We don’t need to figure out which API calls we need to mock, which allows us to create meaningful integration tests instead of mocking the whole modal.



Supported Libraries

When creating a testkit you’ll need to decide on which libraries you are willing to support. For example, are you going to provide multiple testkits for enzyme and @testing-library/react? Are you going to provide an end-to-end testkit that will support puppeteer as well?

When creating a testkit we need to make sure we know what technologies/libraries the testkit covers and what kinds of tests our consumers are willing to create.



Test Your Testkit

By testing your component with your own testkit you are making sure that the testkit works. You don’t want to give your consumers a broken testkit.

The main reason you want an up-to-date testkit is because when you break another team’s implementation, it will be reflected in their test suite immediately, since they are using the testkit as well.


If for any reason your component/function tests don’t have sufficient coverage of your testkit’s functionality - create a dummy component/function that mimics your consumer’s functionality and test with the testkit. It should use all of your testkit’s functionality, otherwise - delete this functionality, your users won’t use it either.



 

Conclusions


A properly written testkit allows our consumers an easy and meaningful testing experience, which will lead to better test coverage and, hopefully, a decrease in production incidents.


Key points to remember:

  • We should decide in advance on the testkit’s scope.

  • Mock API calls and allow users to mock your API calls with custom parameters if needed.

  • Expose DOM interactions.

  • Testing our own testkit.

  • Thumb rule - Whenever I am writing a component that is going to be used by another team, I will consider a testkit.


Thanks for reading,

I hope that your next component will start with a testkit.


 

This post was written by Liad Madmon


 

For more engineering updates and insights:



Recent Posts

See All

1 Comment


Guy Kroizman
Guy Kroizman
Jan 20, 2022

Thanks Liad, Very nice.

You might consider the priority of queries of react testing library: https://testing-library.com/docs/queries/about/#priority querying for a component by its ID is the least preferred way.

If you were to query for a component by role: * you decouple yourself from the implementation. * you are testing like a user is using the application. * You might discover accessibility issues.

Like
bottom of page