Asynchronous Sad Path Testing in React Using Jest and The Driver Pattern

Eyal Eizenberg
Wix Engineering
Published in
7 min readMar 22, 2020

React is dominating the Javascript “frameworks world”. It has surpassed Angular in it’s popularity, amount of 3rd party libraries, downloads and even job listings. If you are using React, then you should also be testing your code. However, there are some best practices and concepts which should be taken into account, especially when you are testing asynchronous code. In this post we will cover the concept of testing the “sad path” of your asynchronous React component.

Tools Of The Trade

Before we get into the fun, it’s important to know which tools to use. The following setup I use is quickly becoming the ‘standard’ way to test React components and (at least when I wrote this) I believe it’s the easiest one to get started with.

I test React components mostly using Enzyme and Jest. If you don’t know about Enzyme & Jest then you should stop reading and head over to their respective websites and read about them. They are very easy to use and are backed by the good people of AirBnB (Enzyme) and Facebook (Jest). You can also use the React Testing Library or any other library you like. The concepts are the same.

In addition, I use Typescript in all my projects. Typescript has helped me avoid many bugs and together with many unit tests has truly brought stability to the Javascript galaxy. Again, if you are not using Typescript, please take some time to try it out. It really is a game changer.

“Standard” Testing with Enzyme

Let’s look at this simple component:

All it does is accept a name prop and renders a div with a header which says “Welcome!” and a span with the name we supplied. A test file for this component would look like this:

As we can see in this example, we are creating a props object (line 6) which we are passing to the HelloPerson component and shallow rendering it (line 10). Then we are finding the html element with the id ‘name’ and getting the text from it (line 11). Last, we are expecting the name we got from the html element to be the same name we’ve used in the props. This example works fine, however, this quickly leads to messy tests with a lot of lines of code and will quickly become completed when we have even a little bit more complex components. In addition, this can quickly make our tests have repetitive code which we want to avoid.

The Component Driver Pattern

I love having 1 to 2 lines of code per test and using natural language as much as possible. It keeps the tests simple and easy to understand later on if they fail. In order for this to work, I use “The Driver Pattern”.

The driver pattern basically means that we have an additional class which is responsible of “bridging the gap” between our test file and our component. It will help our tests be unaware of the inner works of a component. In addition, if we change something in the component which causes the test to fail, we will just have to amend the driver and the tests will just pass again when the same logic works.

Now let’s look at what a driver for this component would look like:

Our driver imports the component and Enzyme’s shallow rendering method. As you can see, we have 3 public properties which are exposed to us from the driver: given, when, get. (I like how even before writing the test we can already envision how the test will look like).

The givenproperty will hold methods which will allow us to set pre-conditions before something takes place. This is a classic place to have methods which will set the props which are going to be passed down to our component. In our example we have the name method which accepts a string and will set it to the name prop.

The when property will hold methods of “events” which will take place like rendered, clicked, hovered, etc. In our example, I only have the rendered method for now.

Notice how the methods in the given and when objects return this. This pattern allows us to easily chain methods as you will see in the tests below.

The get property will hold methods which will give our tests access to the “output” of the component in a “black box” fashion.

So now that we have a component and a driver, this is what our test file will look like:

Notice that our test only imports the driver, we have no direct dependency on the HelloPerson component. In lines 4 to 8 I am initializing a new HelloPersonDriver for each of our tests in order to make sure we don’t have any leftovers from test to test.

Now, let’s look at our test. We are “giving” the driver the name “Jenia” and then we are telling it to go ahead and render (line 11). Then, we are expecting the renderedName to be the name which we just gave the driver — “Jenia”. Remember when I said I like tests to be 1–2 lines long? 😎

The nice part about this pattern is that if we change the logic of the component, the test will fail, then we will change our driver and then the test will pass. So if for example we decide that instead of using an id property for the span, we want to use a class called name-holder will just change our driver’s renderedName method to look something like this:

this.wrapper.find('.name-holder').text()

And then like magic, our failing test will pass again. This pattern is very useful when doing test driven development as it will be soon be evident in our asynchronous testing part.

Testing Asynchronous Code

It is very common to have the component call some method when it mounts. Let’s say in our component, we will call a method which reports that the user has logged in if the user’s name is “Eyal”. Our component might look like this now:

A common mistake which I see many developers do is checking if an XHR request took place using a tool like Nock. Nock is a great tool I would use it to test out the module which actually makes the XHR requests — data-service in this case. The component’s test or driver should not be aware of the inner logic of the reportUserLogin method. Using Nock here will actually make this more of an integration test rather than a unit test.

This is where Jest is really handy. You can either use Jest mocks or Jest spyOn. There are some subtle differences and you should read about them in the docs. In this example I am going to use spyOn. This method is really useful for knowing when another method has been called or not. There is an important thing to consider here — if you don’t specify the return value, the original method will still be called.

Our updated driver looks like this now:

We’ve imported the dataService in line 3 and now we can spy on it’s methods. In line 20 we are spying on the reportUserLogin method and using the mockReturnValue in order to prevent the original method from being called. Do notice that if you are using Typescript you will need to return the same type as the original method (which is a good idea regardless). Last, we’ve added the reportSpy method to the get object.

Again, another very short test which is human readable and easy to understand.

Testing the Sad Path

Testing the sad path usually refers to testing what happens if something went wrong. In addition, it can also refer to checking if something should not take place as opposed to the ‘happy path’ which we will probably check. In our case, we would want to check that if the name is not ‘Eyal’, the fetchData method will NOT be called.

Let’s add our sad path test:

As you can see we now have a test which checks that if the name is not “Eyal”, the fetchSpy will not be called.

The Nock Sad Path Issue

If you remember, I wrote earlier about the misuse of Nock. If we were using Nock here, we would need to setup Nock to intercept this specific request which is a Promise and then check if that interceptor has been called. If the fetchData method itself is a Promise, our test will probably fail because we we are still in the same event loop as you can see in this Stack Overflow question. More information on Promises and event loops can be found here. This can be especially problematic if we are thinking about the sad path.

If we would have been using Nock here and not mocked the fetchData method, our sad path test would always pass because the promise is not yet called in our event loop. If we wanted to check in the next event loop if the Nock interceptor was not called we would have to use setImmediate or setTimeout with 0 ms to move to the next event loop:

As you can see in line 1, we are using the done variable which basically tells Jest that we are going to “tell it” when the test is done. We are using setImmediate in line 3 in order to “move to the next event loop” and then check our expectation (line 4). Finally, we are executing the done() function in order to tell Jest that the test is finished.

Not only is this more cumbersome and more lines of code, it’s not really readable and seems like voodoo. Junior developers or those who are not familiar with the inner works of promises and event loops will have no clue what’s going on here.

Conclusion

The combination of Typescript, Jest and Enzyme is a powerful tool which helps test our React components. You should stick to the concepts of unit testing and check only the component/module which you are currently focusing on — Jest makes this super easy with it’s great mocking/spying mechanism. Having proper typings will help you make sure that your different modules are interacting properly with each other. The combination of these tools with the “The Driver Pattern” helps to “tame” these tests and make them “human readable” and easy to understand in the future.

Side note: integration/end to end tests are also important and will help you gain even more confidence in your code.

Thanks for reading! Follow me on Medium and Twitter.

--

--