如何在Jest中使用async / await对Promise catch()方法行为进行单元测试?

时间:2017-05-20 11:01:03

标签: reactjs unit-testing async-await jestjs es6-promise

说我有这个简单的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)

我错过了什么?

4 个答案:

答案 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>");});