JavaScript 测试系列实战(二):深层渲染和快照测试

时间:2022-07-24
本文章向大家介绍JavaScript 测试系列实战(二):深层渲染和快照测试,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

在上一篇教程中,我们已经介绍了使用 Enzyme 测试 React 组件的基本知识。今天,我们将更深入地挖掘并学习如何测试组件的 Props,如何(以及为什么)使用 mount 函数,以及什么是 Jest 快照测试。

测试组件的 Props

在前一篇文章中,我们已经测试了传递 Props 给组件的情况。但实际上,我们可以直接测试 Props。让我们回到之前写的 ToDoList 组件,但是这次我们使用一个 Task 组件。

Task 组件的代码如下:

// src/Task.js
import React from 'react';

const Task = (props) => (
  <li id={props.id}>{props.name}</li> 
);

export default Task;

修改后的 TodoList 组件代码如下:

// src/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;

然后我们来测试 ToDoList 组件是否能够渲染 Task 组件并传递正确的 Props:

// src/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);
      })
    })
  });
});

通过上面的测试代码,我们可以确定 Task 组件从 ToDoList 收到了正确的 Props。由于 toDoListInstancetaskInstance 都是继承自 Enzyme 浅包装器 ShallowWrapper,因此可以调用 props 方法来获取一个组件传入的 Props。不仅如此,我们还可以检查组件状态,甚至更改状态。想要查看可用函数的完整列表,可以浏览 Enzyme 文档 https://github.com/airbnb/enzyme/blob/master/docs/api/shallow.md。

mount 完全渲染

但是,如果我们想测试 Task 组件中 li 标签的实际内容呢?也许我们可以像下面这样写:

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);
}

如果运行测试,我们会遇到一个错误:

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 |  });

这里之所以失败,是因为浅层渲染 shallow 的局限性:子组件 Task 将根本不会渲染,因此就无法判断是否渲染出正确的内容。

针对浅层渲染的局限性,Enzyme 提供了完全渲染函数 mount

const toDoListInstance = mount(
  <ToDoList tasks={tasks}/>
);

上面的代码会将完整的 ToDoList 和它所有子组件一起渲染。因此,之前失败的测试现在就会通过。

由于 mount 函数会模拟实际的 DOM,渲染成本更高,因此运行测试会花费更多的时间。通常我们会在集成测试中使用 mount 函数,测试组件之间如何协同工作,而不仅仅是作为独立的单元。

如果你不了解单元测试和集成测试这两个术语,可以看下本系列第一篇教程。

在测试与 DOM 的交互或者在处理高阶组件时,mount 函数也可以派上用场。_Mount 使用 DOM 实现的模拟,Jest 默认使用的是 jsdom。我们可以通过调整 testEnvironment 属性更改。

快照测试

快照测试是 Jest 的一大招牌功能。所谓快照,可以简单地理解成是我们应用的一个**“代码截图”**。当我们运行快照测试时,Jest 将会渲染组件并创建其快照文件。这个快照文件包含渲染后组件的整个结构,并且应该与测试文件本身一起提交到代码库。当我们再次运行快照测试时,Jest 会将新的快照与旧的快照进行比较,如果两者不一致,测试就会失败,从而帮助我们确保用户界面不会发生意外改变。

在 TodoList 的测试代码中添加快照测试:

// src/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();
    });
  });
});

运行上面的代码后,就会产生 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>
`;

_ 如果我们要对 ToDoList 组件进行任何更改,快照测试就会失败,并且显示当前渲染结果与快照之间的精确差异。如果我们要更新所有失败的快照,可以使用 -u 标志(别名为 --updateSnapshot) 来运行 Jest。输入以下命令,一键更新所有快照:

npm test -- -u

实际上,目前 CRA 默认会在监听模式下运行 Jest,我们可以一个个更新冲突的快照。首先运行 npm test ,然后输入 i 以交互方式更新失败的快照。官方的 Jest 文档提供了一个动画来展示这个过程:

interactiveSnapshotUpdate.gif

如果你的 CRA 版本比较老,可以通过 npm test -- --watchAll 来进入 Jest 监听模式。

小结

在本文中,我们介绍了如何直接去测试组件的 Props,并学习了 mount 函数和浅层渲染之间的区别。除此之外,我们还介绍了 Jest 快照测试,这是一个非常强大的工具,可以追踪组件渲染方式的变化。在接下来的文章中,我们还将介绍测试中常见的 Mock 技巧——与组件的模拟交互,不见不散!

- END -

● JavaScript 测试系列实战(一):使用 Jest 和 Enzyme 测试 React 组件● 你不知道的 Npm(Node.js 进阶必备好文)● 用动画和实战打开 React Hooks(一):useState 和 useEffect

·END·