使用测试框架进行React应用测试

建立一个网络应用不再是一件简单的事情,前端在功能职责上已经分担了更多的责任。为了让应用开发效率更高,可维护性更高以及更可读,我们一般会使用类似React, Vue或者Angular这类型的框架来开发。

但是,随着代码量的增加,同样会变得更复杂和更有可能出现bug。因此,我们需要学会如何去写测试用例,通过它来帮助提升我们代码的质量。

幸运的是,现在我们已经有了很多的测试方案,对React来说,有一个叫react-testing-library格外优秀,今天我们就来挖掘一下这个宝库。

为什么要用React测试库(React Testing Library, RTL)

基本来说,React测试库(React Testing Library, RTL)是由简单而完整的React DOM测试实用工具组成,这些实用工具带来了良好的测试实践,特别是:

当你的应用写得越多测试用例,它会给你越多的信心。 - Kent C.Dodds

事实上,开发者倾向于测试那些被我们称为实现细节。举个简单的例子来解释它:我们要实现一个计数器,可以加数或减数。这里有一个基于类的实现以及两个它的两个测试:第一个是用Enzyme写的,第二个则是用RTL写的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// counter.js
import React from 'react'

class Counter extends React.Component {
state = { count: 0 }

increment = () => this.setState(({ count }) => ({ count: count + 1 }))

decrement = () => this.setState(({ count }) => ({ count: count - 1 }))

render () {
return {
<div>
<button onClick={this.decrement}> - </button>
<p>{this.state.count}</p>
<button onClick={this.increment}> + </button>
</div>
}
}
}

export default Counter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// counter-enzym.test.js
import React from 'react'
import { shallow } from 'enzyme

import Counter from './counter'

describe('<Counter />', () => {
it('properly increments and decrements the counter', () => {
const wrapper = shallow(<Counter />);
expect(wrapper.state("count")).toBe(0);

wrapper.instance().increment();
expect(wrapper.state("count")).toBe(1);

wrapper.instance().decrement();
expect(wrapper.state("count")).toBe(0);
})
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// counter-rtl.test.js
import React from "react";
import { render, fireEvent } from "@testing-library/react";

import Counter from "./counter";

describe("<Counter />", () => {
it("properly increments and decrements the counter", () => {
const { getByText } = render(<Counter />);
const counter = getByText("0");
const incrementButton = getByText("+");
const decrementButton = getByText("-");

fireEvent.click(incrementButton);
expect(counter.textContent).toEqual("1");

fireEvent.click(decrementButton);
expect(counter.textContent).toEqual("0");
});
});

比较两个测试用例,感觉哪个更好?为什么?如果你没有写过测试,你可能会感觉两个都还好。事实上,这两个测试用例都能保证counter增加或减少。然后,第一个用例在测试实现细节的时候有两个风险:

  • 假阳性:即使代码被破坏测试也能通过。
  • 假阴性:即使代码是正确的也可能测试不通过。

让我们来说明这两个观点。

[明晚待续]

本文翻译自文章,其中有个人意见修改。