说我有这个简单的React组件:
class Greeting extends React.Component {
constructor() {
fetch("https://api.domain.com/getName")
.then((response) => {
return response.text();
})
.then((name) => {
this.setState({
name: name
});
})
.catch(() => {
this.setState({
name: "<unknown>"
});
});
}
render() {
return <h1>Hello, {this.state.name}</h1>;
}
}
鉴于以下答案以及对该主题的更多研究,我已经提出了最终解决方案来测试 resolve()
案例:
test.only("greeting name is 'John Doe'", async () => {
const fetchPromise = Promise.resolve({
text: () => Promise.resolve("John Doe")
});
global.fetch = () => fetchPromise;
const app = await shallow(<Application />);
expect(app.state("name")).toEqual("John Doe");
});
哪个工作正常。我的问题是现在测试 catch()
的情况。以下不起作用,因为我期望它起作用:
test.only("greeting name is 'John Doe'", async () => {
const fetchPromise = Promise.reject(undefined);
global.fetch = () => fetchPromise;
const app = await shallow(<Application />);
expect(app.state("name")).toEqual("<unknown>");
});
断言失败,name
为空:
expect(received).toEqual(expected)
Expected value to equal:
"<unknown>"
Received:
""
at tests/components/Application.spec.tsx:51:53
at process._tickCallback (internal/process/next_tick.js:103:7)
我错过了什么?
答案 0 :(得分:8)
该行
const app = await shallow(<Application />);
两个测试中的都不正确。这意味着浅薄的回归承诺,而不是。因此,您并不是真的在等待构造函数中的promise链来根据需要进行解析。首先,将获取请求移动到componentDidMount,其中React docs建议触发网络请求,如下所示:
import React from 'react'
class Greeting extends React.Component {
constructor() {
super()
this.state = {
name: '',
}
}
componentDidMount() {
return fetch('https://api.domain.com/getName')
.then((response) => {
return response.text()
})
.then((name) => {
this.setState({
name,
})
})
.catch(() => {
this.setState({
name: '<unknown>',
})
})
}
render() {
return <h1>Hello, {this.state.name}</h1>
}
}
export default Greeting
现在我们可以通过直接调用componentDidMount来测试它。由于ComponentDidMount正在返回promise,因此await将等待promise链解析。
import Greeting from '../greeting'
import React from 'react'
import { shallow } from 'enzyme'
test("greeting name is 'John Doe'", async () => {
const fetchPromise = Promise.resolve({
text: () => Promise.resolve('John Doe'),
})
global.fetch = () => fetchPromise
const app = shallow(<Greeting />)
await app.instance().componentDidMount()
expect(app.state('name')).toEqual('John Doe')
})
test("greeting name is '<unknown>'", async () => {
const fetchPromise = Promise.reject(undefined)
global.fetch = () => fetchPromise
const app = shallow(<Greeting />)
await app.instance().componentDidMount()
expect(app.state('name')).toEqual('<unknown>')
})
答案 1 :(得分:0)
另一种方法,如果您不想调用完成,则将下一个承诺状态返回到jest。根据断言的结果(期望),测试用例将失败或通过。
e.g
describe("Greeting", () => {
test("greeting name is unknown", () => {
global.fetch = () => {
return new Promise((resolve, reject) => {
process.nextTick(() => reject());
});
};
let app = shallow(<Application />);
return global.fetch.catch(() => {
console.log(app.state());
expect(app.state('name')).toBe('<unknown>');
})
});
});
答案 2 :(得分:0)
通过这个片段的外观
.then((response) => {
return response.text();
})
.then((name) => {
this.setState({
name: name
});
})
似乎文本会返回一个字符串,然后在下一个'then'块中显示为name参数。或者它本身会返回一个承诺吗?
您是否考虑过jest的spyOn
功能?这将有助于您不仅模拟获取部分,而且还断言setState
方法被称为正确的次数和预期值。
最后,我认为React不鼓励在constructor
内制作副作用。构造函数可能用于设置初始状态和其他变量。 componentWillMount
应该是要走的路:)
答案 3 :(得分:0)
最近,我遇到了同样的问题,并最终通过以下方式解决了它 (以您的代码为例)
test.only("greeting name is 'John Doe'", async () => {
const fetchPromise = Promise.resolve(undefined);
jest.spyOn(global, 'fetch').mockRejectedValueOnce(fetchPromise)
const app = await shallow(<Application />);
await fetchPromise;
expect(app.state("name")).toEqual("<unknown>");});