52

JavaScript testing tutorial – part three. Testing props, the mount function and...

 5 years ago
source link: https://www.tuicool.com/articles/hit/ia2uQv6
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Hello! In the previous part of the tutorial, we’ve covered the very basics of testing React components with Enzyme. Today we will dig a bit deeper and learn how to test props, how (and why) use the mount function and what are the snapshot tests. Let’s go!

Testing props

In the previous article, we’ve tested the outcome of passing some props. But actually, we can test the props directly. Let’s get back to our ToDoList component, but this time let’s use a Task component.

ToDoList.js

import React from 'react';
import Task from "../Task/Task";
 
const ToDoList = (props) => {
  return (
    <ul>
      {
        props.tasks.map(task =>
          <Task key={task.id} id={task.id} name={task.taskName}/>
        )
      }
    </ul>
  )
};
 
export default ToDoList;

We’re about to test if the ToDoList component renders the Task components and passes them the task names.

ToDoList.test.js

import React from 'react';
import { shallow } from 'enzyme';
import ToDoList from './ToDoList';
 
describe('ToDoList component', () => {
  describe('when provided with an array of tasks', () => {
    it('passes them to the Task components', () => {
      const tasks = [
        {
          id: 0,
          name: 'Wash the dishes'
        },
        {
          id: 1,
          name: 'Make the bed'
        }
      ];
      const toDoListInstance = shallow(
        <ToDoList tasks={tasks}/>
      );
      toDoListInstance.find('Task').forEach(taskInstance => {
        const taskProps = taskInstance.props();
        const matchingTask = tasks.find(task => task.id === taskProps.id);
        expect(taskProps.name).toBe(matchingTask.name);
      })
    })
  });
});

Thanks to our test we can be sure that the Task components receive the right props from the ToDoList .

We can call the props function thanks to the fact that both toDoListInstance and taskInstance inherit from the ShallowWrapper . In the same manner, you can check the state, or even change it. For a full list of available functions, you can visit the documentation .

But what if we would like to test the actual contents of the <li> tags in the Task component?

toDoListInstance.find('Task').forEach(taskInstance => {
  const taskProps = taskInstance.props();
  const matchingTask = tasks.find(task => task.id === taskProps.id);
  const listItem = taskInstance.first('li');
  expect(listItem.text()).toBe(matchingTask.name);
})

After running the test you will encounter an error! It looks like that:

 FAIL  app/components/ToDoList/ToDoList.test.js
  ● ToDoList component › when provided with array of tasks › passes them to the Task components
 
    expect(received).toBe(expected) // Object.is equality
 
    Expected: "Wash the dishes"
    Received: "<Task />"
 
      23 |         const matchingTask = tasks.find(task => task.id === taskProps.id);
      24 |         const listItem = taskInstance.first('li');
    > 25 |         expect(listItem.text()).toBe(matchingTask.name);
         |                                 ^
      26 |       })
      27 |     })
      28 |   });

Rendering with the mount function

This fails because we are using a shallow rendering there. With that being the case, the child component will not render at all. That’s why the test above failed and you need to be aware of that limitation of the  shallow rendering .

Mount uses a simulation of a DOM implementation and  Jest by default uses  jsdom . You can change it through the testEnvironment property .

In the previous versions of Enzyme the lifecycle methods weren’t called during the shallow rendering. It changed in the Enzyme 3.0
const toDoListInstance = shallow(
  <ToDoList tasks={tasks}/>
);

Running the code above will cause a whole ToDoList component to render with all its children. As a result, the test that previously was failing will pass now.

Since the mount function renders more and imitates actual DOM, therefore tests will take more time. Using the mount function might mean that you are not  unit testing anymore and now you are doing  integration tests. It is due to that fact, that with the  mount function, you can test how the components work together and not just as separate units.

For a comparison of the terms unit testing and  integration testing checkout of the first part of the tutorial .

It might prove to be also useful when testing interactions with DOM or when working with the Higher-Order Components.

To read more about Higher-Order components check out the official guide and a really descriptive post by David Kopal

Snapshot testing

Tests that include using snapshots are quite useful. During such tests, the component is rendered and a  snapshot of it is created. It contains a whole structure of the rendered component that should be committed to the repository along with the test itself. When running the snapshot test again, a new snapshot will be compared to the old one. If they will differ, the test will fail. It will help you to make sure that your User Interface does not change unexpectedly.

ToDoList.test.js

import React from 'react';
import { shallow } from 'enzyme';
import ToDoList from './ToDoList';
 
describe('ToDoList component', () => {
  describe('when provided with an array of tasks', () => {
    it('should render correctly', () => {
      const tasks = [
        {
          id: 0,
          name: 'Wash the dishes'
        },
        {
          id: 1,
          name: 'Make the bed'
        }
      ];
      const toDoListInstance = shallow(
        <ToDoList tasks={tasks}/>
      );
      expect(toDoListInstance).toMatchSnapshot();
    });
  });
});

Running the code above will create a file called ToDoList.test.js.snap

ToDoList.test.js.snap

// Jest Snapshot v1, https://goo.gl/fbAQLP
 
exports[`ToDoList component when provided with array of tasks should render correctly 1`] = `
<ul>
  <Task
    id={0}
    key="0"
    name="Wash the dishes"
  />
  <Task
    id={1}
    key="1"
    name="Make the bed"
  />
</ul>
`;

If you will make any changes to the ToDoList component the test will fail and will present you with an exact difference between snapshots which is very useful.

To update all of your failing snapshots , you can run  Jest with the  - u flag (alias for  -- updateSnapshot ). To do so, type  npm run test -- - u .

You can also run Jest in the watch mode which will allow you to update all of the conflicting snapshots , one by one. In order to do it, run  npm run test -- -- watchAll and then choose  i to update failing snapshots interactively. The official Jest documentation has a quite good animation presenting this process.

Snapshot testing can be a very powerful tool to keep track of changes to your components. It will prevent you from changing a component in an unexpected way, forcing you to look through the changes that you’ve made in order to accept it or fix an issue. Therefore it can be useful as a utility to monitor your code.

Summary

In this article, we’ve covered testing the props of our components and learned the difference between the mount function and the shallow rendering . Aside from that, we’ve covered snapshot testing which can be a very useful tool to track the changes of the way that your components are rendered. In the upcoming articles, we will also cover simulating interactions with our components, so stay tuned!


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK