Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to TEST async calls made in componentDidMount that set the state of React Component

What is the best way to test that an async call within componentDidMount sets the state for a React component? For context, the libraries I'm using for testing are Mocha, Chai, Enzyme, and Sinon.

Here's an example code:

/* 
 * assume a record looks like this:
 * { id: number, name: string, utility: number }
 */

// asyncComponent.js
class AsyncComponent extends React.Component {
    constructor(props) {
        super(props);

        this.state = {
            records: []
        };
    }

    componentDidMount() {
        // assume that I'm using a library like `superagent` to make ajax calls that returns Promises

        request.get('/some/url/that/returns/my/data').then((data) => {
            this.setState({
                records: data.records
            });
        });
    }

    render() {
        return (
            <div className="async_component">
                { this._renderList() }
            </div>
        );
    }

    _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", () => {
    it("should render correctly after setState in componentDidMount executes", () => {
        // I'm thinking of using a library like `nock` to mock the http request

       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 />);

       // NOW WHAT? This is where I'm stuck.
    });
});
like image 345
wmock Avatar asked Aug 03 '16 20:08

wmock


People also ask

Can I call async function in componentDidMount?

Here are the steps you need to follow for using async/await in React: configure babel. put the async keyword in front of componentDidMount. use await in the function's body.

Can you set state in componentDidMount?

You may call setState() immediately in componentDidMount() . It will trigger an extra rendering, but it will happen before the browser updates the screen.


1 Answers

So, what you are really trying to test is that based on some mock data it "should render correctly ...".

As some people pointed out, a good way to achieve that is by placing the data fetching logic into a separate container and have a "dumb" presentation component that only knows how to render props.

Here is how to do that: (I had to modify it a bit for Typescript with Tslint, but you'll get the idea)

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

Now you can test MyComponent rendering by giving your mock data as props.

like image 113
Dima G Avatar answered Oct 13 '22 01:10

Dima G