测试componentDidMount
内的异步调用设置React组件状态的最佳方法是什么?对于上下文,我用于测试的库是{ {1}},Mocha
,Chai
和Enzyme
。
以下是一个示例代码:
Sinon
答案 0 :(得分:2)
所以,你真正想要测试的是基于一些模拟数据它“应该正确呈现......”。
正如一些人所指出的,实现这一目标的一个好方法是将数据获取逻辑放入一个单独的容器中,并拥有一个只知道如何呈现props
的“哑”表示组件。
以下是如何做到这一点: (我必须使用Tslint对Typescript进行一些修改,但你会得到这个想法)
export interface Props {
// tslint:disable-next-line:no-any
records: Array<any>;
}
// "dumb" Component that converts props into presentation
class MyComponent extends React.Component<Props> {
// tslint:disable-next-line:no-any
constructor(props: Props) {
super(props);
}
render() {
return (
<div className="async_component">
{this._renderList()}
</div>
);
}
_renderList() {
// tslint:disable-next-line:no-any
return this.props.records.map((record: any) => {
return (
<div className="record" key={record.name}>
<p>{record.name}</p>
<p>{record.utility}</p>
</div>
);
});
}
}
// Container class with the async data loading
class MyAsyncContainer extends React.Component<{}, Props> {
constructor(props: Props) {
super(props);
this.state = {
records: []
};
}
componentDidMount() {
fetch('/some/url/that/returns/my/data')
.then((response) => response.json())
.then((data) => {
this.setState({
records: data.records
});
});
}
// render the "dumb" component and set its props
render() {
return (<MyComponent records={this.state.records}/>);
}
}
现在,您可以通过将模拟数据作为道具来测试MyComponent
渲染。
答案 1 :(得分:0)
忽略,理智,再次思考结构的建议,一种方法可能是:
mount
功能done
回拨setImmediate
),这将确保您的承诺得到解决简而言之:
// asyncComponentTests.js
describe("Async Component Tests", () => {
it("should render correctly after setState in componentDidMount executes", (done) => {
nock("http://some.url.com")
.get("/some/url/that/returns/my/data")
.reply(200, {
data: [
{ id: 1, name: "willson", utility: 88 },
{ id: 2, name: "jeffrey", utility: 102 }
]
});
const wrapper = mount(<AsyncComponent />);
// make sure state isn't there yet
expect(wrapper.state).to.deep.equal({});
// wait one tick for the promise to resolve
setImmediate(() => {
expect(wrapper.state).do.deep.equal({ .. the expected state });
done();
});
});
});
注意:
我对nock没有任何线索,所以我假设你的代码是正确的
答案 2 :(得分:0)
国际海事组织,这实际上是一个常见问题,由于承诺和componentDidMount
而显得更加复杂:
您正在尝试测试仅在另一个函数范围内定义的函数。即你应该将你的功能分开并单独测试。
<强>组件强>
class AsyncComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
records: []
};
}
componentDidMount() {
request.get('/some/url/that/returns/my/data')
.then(this._populateState);
}
render() {
return (
<div className="async_component">
{ this._renderList() }
</div>
);
}
_populateState(data) {
this.setState({
records: data.records
});
}
_renderList() {
return this.state.records.map((record) => {
return (
<div className="record">
<p>{ record.name }</p>
<p>{ record.utility }</p>
</div>
);
});
}
}
单元测试
// asyncComponentTests.js
describe("Async Component Tests", () => {
describe("componentDidMount()", () => {
it("should GET the user data on componentDidMount", () => {
const data = {
records: [
{ id: 1, name: "willson", utility: 88 },
{ id: 2, name: "jeffrey", utility: 102 }
]
};
const requestStub = sinon.stub(request, 'get').resolves(data);
sinon.spy(AsyncComponent.prototype, "_populateState");
mount(<AsyncComponent />);
assert(requestStub.calledOnce);
assert(AsyncComponent.prototype._populateState.calledWith(data));
});
});
describe("_populateState()", () => {
it("should populate the state with user data returned from the GET", () => {
const data = [
{ id: 1, name: "willson", utility: 88 },
{ id: 2, name: "jeffrey", utility: 102 }
];
const wrapper = shallow(<AsyncComponent />);
wrapper._populateState(data);
expect(wrapper.state).to.deep.equal(data);
});
});
});
注意:我已单独从文档编写单元测试,因此使用shallow
,mount
,assert
和{{ 1}}可能不是最佳做法。