Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to unit test Promise catch() method behavior with async/await in Jest?

Say I have this simple React component:

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>;
    }
}

Given the answers below and bit more of research on the subject, I've come up with this final solution to test the resolve() case:

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

Which is working fine. My problem is now testing the catch() case. The following didn't work as I expected it to work:

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

The assertion fails, name is empty:

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)

What am I missing?

like image 810
rfgamaral Avatar asked May 20 '17 11:05

rfgamaral


People also ask

How do you test asynchronous code with Jest?

You can combine async and await with .resolves or .rejects . await expect(fetchData()).rejects.toMatch('error'); }); In these cases, async and await are effectively syntactic sugar for the same logic as the promises example uses.

How do I resolve a promise in Jest?

You can also use the . resolves matcher in your expect statement, and Jest will wait for that promise to resolve. If the promise is rejected, the test will automatically fail. test('the data is peanut butter', () => { expect.

Which of the following methods are used to test an asynchronous code by using Jest?

Testing With Async / Await As we saw in the previous section, Jest will know that we are dealing with asynchronous code if we return a Promise object form the test function. If we declare the test function as async , it will implicitly make the function to return a Promise.


1 Answers

The line

const app = await shallow(<Application />);

is not correct in both tests. This would imply that shallow is returning a promise, which it does not. Thus, you are not really waiting for the promise chain in your constructor to resolve as you desire. First, move the fetch request to componentDidMount, where the React docs recommend triggering network requests, like so:

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

Now we can test it by calling componentDidMount directly. Since ComponentDidMount is returning the promise, await will wait for the promise chain to resolve.

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>')
})
like image 99
TLadd Avatar answered Oct 22 '22 23:10

TLadd