Enzyme 组件测试实战:这个玩具由我造
在前端开发中,我们经常需要开发各种组件,例如按钮、表单、轮播图等等。然而,这些组件的功能是繁多复杂的,我们如何保证它们的正确性呢?这时候,组件测试就成了必不可少的环节。Enzyme 是一个流行的 React 组件测试工具之一,它提供了非常丰富的 API 可以帮助我们测试组件。本文将介绍 Enzyme 的基本使用以及实战操作,帮助大家更好地应用 Enzyme 进行组件测试。
一、Enzyme 简介
Enzyme 是 Airbnb 开源的用于 React 应用程序测试的 JavaScript 测试工具库。它的 API 设计专门针对 React 组件使用场景,提供了一些实用的组件渲染和交互操作方法,使得开发者可以更加方便地对 React 组件进行测试。
Enzyme 支持 Shallow Rendering、Full DOM Rendering 和 Static Rendering 三种测试方式。最常用的是 Shallow Rendering,它不需要使用真实浏览器环境来进行渲染,而是使用类似快照测试的方式,只渲染当前组件的最外层,不会去渲染子组件,从而避免了无关的干扰,也提高了测试速度。而 Full DOM Rendering 则需要使用 jsdom 等类似浏览器环境才能进行完整的组件渲染测试,一般用于模拟用户真实操作的场景。
二、Enzyme 的安装和使用
安装 Enzyme 很简单,只需要使用 npm 或 yarn 安装即可。以 npm 为例,执行以下命令即可安装:
npm install enzyme enzyme-adapter-react-16 --save-dev
其中,enzyme-adapter-react-16 是对 React 16 及以上版本的适配工具,如果你的 React 版本不同,还需要安装相应版本的适配器,具体可参考 Enzyme 的官网文档。
Enzyme 的使用非常简单,只需要导入相应的工具方法即可。以 Shallow Rendering 为例,我们可以用 shallow 方法来进行组件渲染,例如:
// www.javascriptcn.com code example
import React from 'react';
import { shallow } from 'enzyme';
import Button from './Button';
describe('<Button />', () => {
it('should render correctly with props', () => {
const wrapper = shallow(<Button type="primary">Click me</Button>);
expect(wrapper).toMatchSnapshot();
});
});上面的代码中,我们首先从 Enzyme 中导入 shallow 方法,然后在测试用例中使用 shallow 方法对 Button 组件进行渲染,同时传入组件的 Props。最后,我们运用 Jest 中的 toMatchSnapshot 方法进行快照测试,来验证组件是否正确渲染。
三、Enzyme 实战操作
为了更好地理解 Enzyme 的使用方法,下面我们将以一个具体的例子来进行实战操作。假设我们要开发一个简单的轮播图组件,该组件的功能有:
- 可以配置是否自动轮播
- 可以配置自动轮播时间间隔
- 可以配置轮播速度
- 可以配置是否显示分页器等
如图所示:

下面我们将从以下方面来介绍如何使用 Enzyme 对该组件进行测试:
测试组件基本渲染和 Props 是否正确传递
测试组件自动轮播是否正常工作
测试组件手动切换和分页器是否正常工作
测试组件基本渲染和 Props 是否正确传递
我们可以用 Enzyme 的 shallow 方法来进行组件渲染测试,例如:
// www.javascriptcn.com code example
import React from 'react';
import { shallow } from 'enzyme';
import Carousel from './Carousel';
describe('<Carousel />', () => {
it('should render correctly with props', () => {
const wrapper = shallow(<Carousel autoPlay={true} interval={3000} speed={500} showPager={true} items={[{},{},{}]} />);
expect(wrapper).toMatchSnapshot();
});
});上面的代码中,我们使用 shallow 方法来渲染 Carousel 组件,并传递相应的 Props。最后通过快照测试,来验证组件是否正确渲染。
- 测试组件自动轮播是否正常工作
我们可以通过手动修改 Props 中的 autoPlay 值为 true,来进行自动轮播测试。同时,我们需要等待一段时间后再进行断言,来验证组件是否正常自动轮播。例如:
// www.javascriptcn.com code example
describe('<Carousel />', () => {
it('should autoplay correctly', (done) => {
const wrapper = shallow(<Carousel autoPlay={true} interval={1000} speed={500} showPager={false} items={[{},{},{}]} />);
setTimeout(() => {
expect(wrapper.find('.carousel__item--active').length).toBe(1);
done();
}, 2000);
});
});上面的代码中,我们首先使用 shallow 方法来渲染 Carousel 组件,并传递 autoPlay 的值为 true。然后,我们使用 setTimeout 来等待 2 秒,确保组件已经自动轮播了两次。最后,我们使用 expect 断言来判断是否只有一个 carousel__item--active 的样式。
- 测试组件手动切换和分页器是否正常工作
我们可以用 simulate 来模仿用户手动点击操作,从而测试手动切换是否正常工作。例如:
describe('<Carousel />', () => {
it('should switch manually correctly', () => {
const wrapper = shallow(<Carousel autoPlay={false} interval={3000} speed={500} showPager={true} items={[{},{},{}]} />);
expect(wrapper.find('.carousel__pager-item--active').index()).toBe(0);
wrapper.find('.carousel__arrow--right').simulate('click');
expect(wrapper.find('.carousel__pager-item--active').index()).toBe(1);
});
});上面的代码中,我们首先使用 shallow 方法来渲染 Carousel 组件,并传递相应的 Props。然后,我们使用 expect 断言来确认分页器当前的 active 类型是否为第一个。接着,我们使用 simulate 来模仿用户点击右箭头的操作,然后再次使用 expect 断言来确认分页器当前的 active 类型是否为第二个。
最后,我们再来看一下 Carousel 组件的实现:
// www.javascriptcn.com code example
import React, { useState, useEffect, useCallback } from 'react';
import PropTypes from 'prop-types';
import cx from 'classnames';
import './Carousel.css';
const Carousel = ({ autoPlay, interval, speed, showPager, items }) => {
const [activeIndex, setActiveIndex] = useState(0);
const [translateValue, setTranslateValue] = useState(0);
const handlePrevClick = useCallback(() => {
setActiveIndex((activeIndex - 1 + items.length) % items.length);
}, [items.length, activeIndex]);
const handleNextClick = useCallback(() => {
setActiveIndex((activeIndex + 1) % items.length);
}, [items.length, activeIndex]);
useEffect(() => {
if (autoPlay) {
const timer = setInterval(() => {
handleNextClick();
}, interval);
return () => {
clearInterval(timer);
};
}
}, [autoPlay, handleNextClick, interval]);
useEffect(() => {
setTranslateValue(-activeIndex * 100);
}, [activeIndex]);
return (
<div className="carousel">
<div className="carousel__inner" style={{ transform: `translateX(${translateValue}%)`, transition: `transform ${speed}ms` }}>
{items.map((item, index) => (
<div key={index} className={cx('carousel__item', { 'carousel__item--active': index === activeIndex })}>
{item.content}
</div>
))}
</div>
{showPager && (
<div className="carousel__pager">
{items.map((item, index) => (
<div
key={index}
className={cx('carousel__pager-item', { 'carousel__pager-item--active': index === activeIndex })}
onClick={() => setActiveIndex(index)}
></div>
))}
</div>
)}
<div className="carousel__arrow carousel__arrow--left" onClick={handlePrevClick}></div>
<div className="carousel__arrow carousel__arrow--right" onClick={handleNextClick}></div>
</div>
);
};
Carousel.propTypes = {
autoPlay: PropTypes.bool,
interval: PropTypes.number,
speed: PropTypes.number,
showPager: PropTypes.bool,
items: PropTypes.arrayOf(PropTypes.shape({ content: PropTypes.node.isRequired })).isRequired,
};
Carousel.defaultProps = {
autoPlay: false,
interval: 3000,
speed: 500,
showPager: true,
};
export default Carousel;通过上面的实现,我们可以看出,Enzyme 在组件测试中的应用无疑是非常重要的。它能够极大地提高测试的效率和可维护性,使得开发者更加自信地进行前端开发。当然,在实际应用中,Enzyme 的使用可能并不止于此,但是基础的应用也足以满足我们的日常开发需求。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/67d630e0a941bf7134bde712