Saturday , May 8 2021

How to test reactive components using Jest and Enzyme – Bits and Pieces

1. Functional app

Click Here go to the repo and the branch. If you'd like to test directly, go to section 2 of this blog post.

How does the app appear?

As you can see the app is simple. It has a header, an input field that accepts the query and is based on those calls to the Guardian API and returns the results of related articles in a list format.

This is the format of the directory:

Folder structure

Since we only use the React status, I have separated my components into components and containers. The components take care of the presentation code and the containers make the logical side of things like API calls. I will not talk in detail about the set-up as this can be found in my previous post.

Note: I have added more settings / packages to this project, so if something is not clear, you can ask me in the comments 🙂

The research component

path: src / components / Search / index.js

The job of the research component is to show the app's interface to the user. This component has an internal state of value and two class methods handleChange is handleSubmit. The component makes the form and I tie the handleSubmit method for this module. This method ensures that we do not serve the default action of the browser when sending the form (which is sending it to the server). In the input field, I associate the value status with the value attribute so that the component has a memory of what the user has typed. Lego me too handleChange method of attributing onChange to then take the user input and pass it to performSearch method. The reason why it comes through the props is because this method comes from the search container method. You can also write the setState method for value like this:

this.setState ({value:})
let value =
this.setState ({value: value})
With ES6, if the key-value pairs are the same, you can compress it with a single word like I did earlier:
let value =
this.setState ({value})

Then I have a SearchResults component that step into the props that come from the search container that I will show in a bit. But first let's take a look at SearchResults.

The SearchResults component

path: src / components / Search / SearchResults / index.js

The props of the articles are inserted and we map all the results in the articles.

API results in JSON format:

The Guardian API results for the "pie" search term

The search container

path: src / containers / Search / index.js

You can see it SearchContainer has an internal state of articles array. It also has a performSearch class method that takes an event argument to pass it to a named method fetchArticles. I extracted this method in the bees folder:

You can see it SearchContainer has an internal state of articles array. It also has a performSearch class method that accepts a query string argument to pass it to a method called fetchArticles. I extracted this method in the bees folder:

path: src / api / index.js

The reason why I extracted this method will become apparent when we come to test it.

So we get the answer from the API that was completed fetchArticles method, the body text is then analyzed in JSON using the json () method. Returning to the SearchContainer, we get that answer, let's call it data, is setState of that data.response.results to the internal state of articles.

If I had not extracted the method, the performSearch method would look like this:

I hope this diagram makes it easier to see how it works? I apologize for the mega bad photo and low resolution, but I was using a free online photo editor.

Once the results are in, it is passed to the status of the articles, which is then passed to the search component!

This is why we have to call .props on these method methods and performSearch articles in the search component:

Component of the app

To link all these components, we also need an entry app component where we are importing the search container.

Hoping it makes sense! Make a comment below if you are not clear of anything. Without further ado, let's go to the tests!

2. Unit test ✅

Click here to go to the repository and the unit test branch.

First we need to install some packages that we will use to test our React components:

npm i-D enzyme enzyme-adapter-reacts-16 jest

Could you ask why we need to install both Enzyme and Jest? Enzyme provides the test utility functions for React components like shallow, mount is make while Jest is a test runner and an assertion library. Without Jest, there's no way to run Enzyme tests.

An enzymatic adapter is required to ensure compatibility with the different versions of React. Since we are currently using the 16+ version, we need to install enzyme-adapter-react-16.

? Search for the tests of the component units

Let's start with the search component. You must create a Search.test.js file in the search folder. So we have to import the things we need to test and configure our Enzyme adapter.

  • To react: React must always be imported
  • Enzyme, {shallow, mount}: these are the Enzyme methods we will use in our tests
  • Search: we have to import the component we are testing
  • Adapter: to configure the Enzyme adapter

First test!

Here we are testing if the Search component renders. We pass component a shallow () Enzyme method and store is in a wrapper variable. So let's check that this component exists using a Boolean statement. Quite simple, right?

To run the test, go to package.json and add the following script:

"script": {
"test": "jest --verbose",

You can run the tests using the following command:

test npm

The –verbose flag displays the results of individual tests with the test suite hierarchy.

Note that the descriptive block comes first, so the test suite comes under it.

Two other test scripts I added are:

"script": {
"test": "jest --verbose",
"test: watch": "jest --watchAll --verbose",
"test: coverage": "jest --verbose --coverage",

  • Test: clock will run every time there is a change in the test file and / or the component
  • Test: Coverage will show the percentage of code we covered in our tests

Note: to perform these in your terminal you need to specify operating test npm: look or operating test npm: coverage

This step is optional. Feel free to play with it, but it is not necessary for this.

Fantastic, let's go to the next test!

The next thing we are testing is if the user's text input has been echoed. This time we have to go into the performSearch method to support the search component and give it an empty function. We have to do this because the component expects the parent, SearchContainer, to pass a callback.

We find therefore the input node and use simulate API to simulate a change event with the target which is the value state with a "hello" string.

So let's make our statement where we find this input node again and look at its props value and check if it's the same as "hello".

Next test, we want to verify that when the form is sent, the event is canceled, which means that the send function of the default browser is not activated.

Here we make a prevented variable equal to false. So we find a module from the Search component, simulate a Subscribe event in which a preventDefault the method is called and modified prevented variable a true. So we check that this was changed by making an assertion about it.

? Testing the component units of SearchResults

In the same way as before, we need to import the things we need and configure the Enzyme adapter.

For the first test, we are doing the same thing as before, where we check that SearchResults renderings. However, this time I decided to do it using the Snapshot test to show how it works. When you run this test for the first time, it will save the DOM tree returned to a __snapshots__ folder.

Each time the test is run, the current DOM will be compared to the current DOM in the __snapshots__ folder.

Here we are also teasing some data to pass to the prop object in the SearchResults component. Let's check if the SearchResults component makes the same DOM tree of the one created in the __snapshots__ folder using it toMatchSnapShot () method.

The next test I am testing the DOM tree when no data is passed.

The next test, I'm using Enzyme again to verify that the SearchResults component does not break even without the props being passed. We do it by checking that there are not <li> tags like this would have been rendered if the articles had data in and passed through the SearchResults component.

The last test for this component is to verify that even with empty items, the component does not break.

3. Integration test ✅

Click Here go to the repository and the integration test.

The last part of this post is to show how I performed some integration tests in the Search and Search Container component.

Returning to the Research component, we will add an integration test:

Here we are checking if the search component makes the search results when the status of the articles changes.

This test is slightly different since we are using the mount method from Enzyme. Mount provides complete rendering included any child component. Previously we had only used shallow which controls and makes the component in question and does not look at any child component.

Because the SearchResults component is a child of the research component, we have to verify it and make sure articles exists. We pass the component objects of the search component as an empty array. So we set up Props with the array of items to get a WebUrl is webTitle since this is what we map into the SearchResults component. We find therefore the yet tag with his href attribute equal to the same WebUrl when we set the props.

I have previously included this test in the unit test section. However later I asked myself if to use mount I would do an integration test Then I realized that if I'm using it to test the behavior of a child component, then it's an integration test. It would only be a unit test if I am testing only the parent component.

? Search container integration test

While we make fun of the answer from the API, we need to make some additional configurations. We will use the manual simulation function of Jest so that the start of the test file should start in this way:

path: src / containers / Search / Search.test.js

In my api directory I have an API call method:

path: src / api / index.js

I have also included a mocked version of this that we will use later for our test:

path: src / api / __ mocks __ / index.js

Here we are resembling the actual response of the API using Promise.resolve (). Comparing with the actual answer, we have an object called answer, followed by results, is WebUrl is webTitle that we use in the search results component. This will make more sense when we arrive at the last test.

The first 2 tests are rather simple. I will skip the first test because it is technically a unit test, similar to some we have done before. The second test we are using mount again to verify that the SearchContainer component has a search component as a child.

The last test consists of using the false! Here we are setting the status of the initial articles as an empty array. So we look for performSearch props from the search component.

Note that this line:

const { performSearch } = wrapper.find (Search) .props ()

it can also be written as:

const performSearch = wrapper.find (Search) .props (& # 39;performSearch& # 39;)

However I'm using Destructive Assignment ES6 here so I do not have to repeat performSearch.

peformSearch is then invoked to return a promise where the length of the articles state is expected to be 10 (since this is what we have previously mocked in our API).

Now when you're doing yours operating test npm: coverage you should get these results:

This concludes my attempt to explain how to test the components of React. Thank you so much for taking the time to read it! Please leave any improvements and suggestions in the comments as I am still learning a lot about React tests and I want to get better with this!

A special thanks to Minh Nguyen for helping me with the tests, Oliver Turner is Tomasz Wegrzanowski for proofreading and suggestions!

Source link

Leave a Reply

Your email address will not be published.