Jest - 如何测试反应方法的输出是否正确?
Jest - How to test output of react methods is correct?
我在尝试了解如何使用 Jest 测试反应文件中方法的输出时遇到问题。我对这种 Web 开发风格完全陌生,因此不胜感激。
我有一个这样的 js 文件:
import * as React from 'react';
import 'es6-promise';
import 'isomorphic-fetch';
export default class FetchData extends React.Component {
constructor() {
super();
this.state = { documents: [], loading: true };
fetch('api/SampleData/GetDocuments')
.then(response => response.json())
.then(data => {
this.setState({ documents: data, loading: false });
});
}
render() {
let contents = this.state.loading ? <p><em>Loading...</em></p>
: FetchData.renderdocumentsTable(this.state.documents);
return <div>
<button onClick={() => { this.refreshData() }}>Refresh</button>
<p>This component demonstrates bad document data from the server.</p>
{contents}
</div>;
}
refreshData() {
fetch('api/SampleData/GetDocuments')
.then(response => response.json())
.then(data => {
this.setState({ documents: data, loading: false });
});
}
static renderdocumentsTable(documents) {
return <table className='table'>
<thead>
<tr>
<th>Filename</th>
<th>CurrentSite</th>
<th>CorrectSite</th>
</tr>
</thead>
<tbody>
{documents.map(document =>
<tr className="document-row" key={document.documentId}>
<td>{document.filename}</td>
<td>{document.currentSite}</td>
<td>{document.correctSite}</td>
</tr>
)}
</tbody>
</table>;
}
}
我基本上希望能够测试 table 返回的列数是否正确,但是我不知道如何使用 Jest 执行此操作。
谢谢,
亚历克斯
我遵循下一个方法:
- Mocking dependencies 被测试组件显式调用。
- 正在使用
shallow()
初始化组件
- 尝试不同的修改
- 检查组件
.toMatchSnapshot()
在 "trying different modifications" 下,我的意思是要么创建具有不同初始 props
的组件,要么与组件的内部元素交互' props
.
test('closes list on button clicked', () => {
let wrapper = shallow(<MyComponent prop1={'a'} prop2={'b'} />);
wrapper.find('button').at(0).simulate('click');
expect(wrapper).toMatchSnapshot();
});
这样你就不需要单独测试方法了。为什么我认为这是有道理的?
虽然所有的方法测试都通过了,但我们仍然不能说它是否作为一个整体起作用(假阳性反应)。
此外,如果我们进行任何重构,如重命名方法,我们的 tests-per-method 将失败。同时,组件可能仍然可以正常工作,我们会花更多的时间来修复测试,只是为了让它们通过(假阴性反应)。
从相反的角度关注 render()
结果(这就是酶适配器在 .toMatchSnapshot()
匹配器的幕后所做的事情)我们测试我们的元素作为 React 项目的一部分所做的事情。
[UPD] 基于您的代码的示例:
describe("<FetchData />", () => {
let wrapper;
global.fetch = jest.fn();
beforeEach(() => {
fetch.mockClear();
});
function makeFetchReturning(documents) {
fetch.mockImplementation(() => Promise.resolve({ json: () => documents }));
}
function initComponent() {
// if we run this in beforeEach we would not able to mock different return value for fetch() mock
wrapper = shallow(<FetchData />);
}
test("calls appropriate API endpoint", () => {
makeFetchReturning([]);
initComponent();
expect(fetch).toHaveBeenCalledWith("api/SampleData/GetDocuments");
});
test("displays loading placeholder until data is fetched", () => {
// promise that is never resolved
fetch.mockImplementation(() => new Promise(() => {}));
initComponent();
expect(wrapper).toMatchSnapshot();
});
test("looks well when empty data returned", () => {
makeFetchReturning([]);
initComponent();
expect(wrapper).toMatchSnapshot();
});
test("reloads documents and displays them", () => {
makeFetchReturning([]);
initComponent();
// no matter what values we include in mock but it should be something non-empty
makeFetchReturning([{fileName: '_', currentSite: '1', correctSite: '2'}]);
wrapper.find('button').at(0).simulate('click');
expect(fetch).toHaveBeenCalledTimes(2);
expect(wrapper).toMatchSnapshot();
})
});
我在尝试了解如何使用 Jest 测试反应文件中方法的输出时遇到问题。我对这种 Web 开发风格完全陌生,因此不胜感激。
我有一个这样的 js 文件:
import * as React from 'react';
import 'es6-promise';
import 'isomorphic-fetch';
export default class FetchData extends React.Component {
constructor() {
super();
this.state = { documents: [], loading: true };
fetch('api/SampleData/GetDocuments')
.then(response => response.json())
.then(data => {
this.setState({ documents: data, loading: false });
});
}
render() {
let contents = this.state.loading ? <p><em>Loading...</em></p>
: FetchData.renderdocumentsTable(this.state.documents);
return <div>
<button onClick={() => { this.refreshData() }}>Refresh</button>
<p>This component demonstrates bad document data from the server.</p>
{contents}
</div>;
}
refreshData() {
fetch('api/SampleData/GetDocuments')
.then(response => response.json())
.then(data => {
this.setState({ documents: data, loading: false });
});
}
static renderdocumentsTable(documents) {
return <table className='table'>
<thead>
<tr>
<th>Filename</th>
<th>CurrentSite</th>
<th>CorrectSite</th>
</tr>
</thead>
<tbody>
{documents.map(document =>
<tr className="document-row" key={document.documentId}>
<td>{document.filename}</td>
<td>{document.currentSite}</td>
<td>{document.correctSite}</td>
</tr>
)}
</tbody>
</table>;
}
}
我基本上希望能够测试 table 返回的列数是否正确,但是我不知道如何使用 Jest 执行此操作。
谢谢, 亚历克斯
我遵循下一个方法:
- Mocking dependencies 被测试组件显式调用。
- 正在使用
shallow()
初始化组件
- 尝试不同的修改
- 检查组件
.toMatchSnapshot()
在 "trying different modifications" 下,我的意思是要么创建具有不同初始 props
的组件,要么与组件的内部元素交互' props
.
test('closes list on button clicked', () => {
let wrapper = shallow(<MyComponent prop1={'a'} prop2={'b'} />);
wrapper.find('button').at(0).simulate('click');
expect(wrapper).toMatchSnapshot();
});
这样你就不需要单独测试方法了。为什么我认为这是有道理的?
虽然所有的方法测试都通过了,但我们仍然不能说它是否作为一个整体起作用(假阳性反应)。 此外,如果我们进行任何重构,如重命名方法,我们的 tests-per-method 将失败。同时,组件可能仍然可以正常工作,我们会花更多的时间来修复测试,只是为了让它们通过(假阴性反应)。
从相反的角度关注 render()
结果(这就是酶适配器在 .toMatchSnapshot()
匹配器的幕后所做的事情)我们测试我们的元素作为 React 项目的一部分所做的事情。
[UPD] 基于您的代码的示例:
describe("<FetchData />", () => {
let wrapper;
global.fetch = jest.fn();
beforeEach(() => {
fetch.mockClear();
});
function makeFetchReturning(documents) {
fetch.mockImplementation(() => Promise.resolve({ json: () => documents }));
}
function initComponent() {
// if we run this in beforeEach we would not able to mock different return value for fetch() mock
wrapper = shallow(<FetchData />);
}
test("calls appropriate API endpoint", () => {
makeFetchReturning([]);
initComponent();
expect(fetch).toHaveBeenCalledWith("api/SampleData/GetDocuments");
});
test("displays loading placeholder until data is fetched", () => {
// promise that is never resolved
fetch.mockImplementation(() => new Promise(() => {}));
initComponent();
expect(wrapper).toMatchSnapshot();
});
test("looks well when empty data returned", () => {
makeFetchReturning([]);
initComponent();
expect(wrapper).toMatchSnapshot();
});
test("reloads documents and displays them", () => {
makeFetchReturning([]);
initComponent();
// no matter what values we include in mock but it should be something non-empty
makeFetchReturning([{fileName: '_', currentSite: '1', correctSite: '2'}]);
wrapper.find('button').at(0).simulate('click');
expect(fetch).toHaveBeenCalledTimes(2);
expect(wrapper).toMatchSnapshot();
})
});