Real-world Units Tests in React with Jest and Enzyme
In our previous articles on React, we talked about unit testing. One of the things that disappoints me a bit with these articles is that I’m forced to make the examples quite simple—pretty much Hello World level kind of stuff. Then, when you get to real-world testing all these cute little examples shatter on the ground cold hard ground of reality.
That’s because the reality of components is quite complex and writing automated tests for more complex components is not easy—at least at the beginning. But, it’s worth the effort to understand tests because if there’s one thing that gives you problem free, high quality, fast development, it’s automated testing. Yes, yes. I really said it.
We talked previously about testing on Hello World type components and also on components that take props. But in the real world, independent components will get data from servers (code that calls to an external server). So let me show you this type of component—a dad joke component, ‘cause I’m a dad joke kinda guy.
First off, follow this link to see how it works. You’ll see a page with a dad joke that uses an API. Notice that I left the sourcemaps open, so you are more than welcome to go into the developer tools and debug a bit. In real-world applications, this would be considered a security weakness.
So, how does it work? We have a simple service that uses a fetch API in order to get the dad joke. This lovely little service can be found in src/services/DadJokeService.service.js and looks like this:
class DadJokeService {
getDadJoke() {
return fetch(`https://icanhazdadjoke.com`, {
headers: {
'Accept': 'application/json'
},
})
.then(res => res.json())
.then(json => json.joke );
}
}
const instance = new DadJokeService();
export default instance;
There’s nothing here that a beginning programmer wouldn’t understand. It’s pure JavaScript and not connected to React or any framework. Just a plain ol’ vanilla service—simple as can be.
Now for the component that uses it. How does it look? Also pretty straightforward. It can be found here: src/components/DadJoke/DadJoke.jsx
import React from 'react';
import DadJokeService from '../../services/DadJokeService.service';
class DadJoke extends React.Component {
constructor() {
super();
this.state = { joke: 'Loading joke...' };
}
componentDidMount() {
DadJokeService.getDadJoke().then(result => this.setState({ joke: result }));
}
render() {
return <p>{this.state.joke}</p>
}
}
export default DadJoke;
Easy as could be. First the component runs Loading and then as soon as it gets a response from the API it displays what it received. It a dynamic component. So how do we check it?
Perhaps it’s better to start with how we don’t check it. We don’t just render it as is. Why? Because if we do, we’ll need to wait for the API with every single test. And that’s a very bad idea in unit testing that’s supposed to concentrate on the component’s basic functioning. We’re not doing some kind of ‘end to end’ testing or integration. We’re supposed to be solely testing the components code. I want to check that the component really calls to the service. I want to check that the service really displays what it received from the service. So I’m 100% certain that I don’t want to be checking the API in this unit test. Units tests are, indeed, only for the testing that specific code.
So how do we do this? Using mock, which is kind of ‘measuring’ the service and its response. Creating a mock is really easy using Jest. We import our module, in this case the service, and the use jest.fn();
Sound complicated? Have a look at the example:
import DadJokeService from '../../services/DadJokeService.service';
DadJokeService.getDadJoke = jest.fn();
DadJokeService.getDadJoke.mockReturnValue('This is mock dadjoke');
This will also be in all the examples out there and in the Jest documentation. This is all well and good but in reality it doesn’t really work this way. Why? Since we’re talking about a service and fetch, I need to make an asynchronous call rather than a synchornous one. So how do we create a mock and simulate an asynchronous response? It’s easy.
- The mock must return promise
- That promise must be fulfilled immediately
Both of these can be accomplished with the following code:
import React from 'react';
import { shallow } from 'enzyme';
import DadJoke from './DadJoke';
import DadJokeService from '../../services/DadJokeService.service';
let element;
describe('DadJoke Component is working normally', () => {
beforeEach(() => {
// mock return promise
const dadJokeResult = Promise.resolve('This is mock dadjoke');
DadJokeService.getDadJoke = jest.fn();
DadJokeService.getDadJoke.mockReturnValue(dadJokeResult);
});
it('renders without crashing', async () => {
element = shallow(<DadJoke />);
expect(element.text()).toContain('Loading');
});
it('implement service', async () => {
element = shallow(<DadJoke />);
await flushAllPromises();
expect(element.text()).toContain('This is mock dadjoke');
});
// Resolving all
const flushAllPromises = () => new Promise((resolve) => setImmediate(resolve()));
});
And you can see just how simple it is. We just create a promise and then make the mock return it. In the end, when I want all the promises to be activated, I just make a quick helper function called flushAllPromises that uses Node.js to do the flush. Notice that the test must be asynchronous which I can achieve by using async in it.
I know it seems a bit complex, but that’s just how it goes in the real world when we leave behind the Hello World stuff. We’ll have to work with promises and sometimes with services or other things to which we need to do mocking.
But the truth is, when I spoke with the head of my team who gave me these real-world code examples, he made the very good point that in reality, we won’t have this dilemma since we use Redux.
In the next article I’ll give you a quick explanation on debugging unit tests, and we’ll continue with using Redux and other similar features.
Previous Article: Testing React Components with Jest and Enzyme | Next article: coming soon! |
About the author: Ran Bar-Zik is an experienced web developer whose personal blog, Internet Israel, features articles and guides on Node.js, MongoDB, Git, SASS, jQuery, HTML 5, MySQL, and more. Translation of the original article by Aaron Raizen.
Recent Stories
Top DiscoverSDK Experts
![User photo](https://discoversdkcdn.azureedge.net/runtimecontent/usersfiles/profilepictures/a6117a46-5b2d-473d-bc51-ff6c8375b40e.png?v131921911703600126)
![User photo](https://discoversdkcdn.azureedge.net/runtimecontent/usersfiles/profilepictures/9bc41e0f-3b36-4133-ba07-4db9407e5086.png?v131187064960498597)
Compare Products
Select up to three two products to compare by clicking on the compare icon () of each product.
{{compareToolModel.Error}}
{{CommentsModel.TotalCount}} Comments
Your Comment