1

I want to test an asynchronous click event in React that does two things: 1. Make an API call to send a POST request. 2. After the post request is successfully made, update the state in top level component.

I want to know what is the best way to test that the events happened in this order.

I was able to use jest-fetch-mock to simulate an API call. But I am not sure how to test if the state update is performed after the API was made. Moreover, the state update is a function being passed down from the top-level component down to this child component.

In Feedback.jsx, updateMembershipStatus is being passed down from another component called ManageMembership.

// This component saves feedback
class Feedback extends PureComponent {
  // ...

  async handleSubmit (event) {
    event.preventDefault();

    const {otherReason} = this.state,
      {id, updateMembershipStatus} = this.props;

    try {
      // 1. Make an API call
      const saveReason = await MembershipStatusAPI.updateOtherCancelReason(id, otherReason);
      // 2. Update membership status
      updateMembershipStatus("Feedback");
    } catch (error) {
      console.error("Oops, handleSubmit failed!", error);
    }
  }

  render () {
    const {isDisabled, otherReason} = this.state;
    return (
      <div>
        <div>
          Please let us know your feedback
        </div>
        <textarea
          className="feedback-input"
          onChange={this.handleChange}
          value={otherReason}
        />
        <button
          disabled={isDisabled}
          onClick={(e) => this.handleSubmit(e)}
          type="submit"
          value="Submit"
        >
          Cancel my membership
        </button>
      </div>
    );
  }
}

ManageMembership.jsx is the top level component

class MembershipManagement extends PureComponent {
  // ... 
  constructor (props) {
    super(props);
    this.state = {
      "membershipStatus": this.getCurrentStatus(),
    };
  }

  updateMembershipStatus = (event) => {
    if (event === "Feedback") {
      this.setState({"membershipStatus": "Pending Cancellation"});
    }
  }
}

My test, FeedbackTest.jsx

describe("<Feedback /> button", () => {
  let handleSubmit = null,
    wrapper = null;

  const updateOtherCancelReason = (url) => {
    if (url === "google") {
      return fetch("https://www.google.com").then(res => res.json());
    }
    return "no argument provided";
  };

  beforeEach(() => {
    handleSubmit = jest.fn();
    wrapper = mount(
      <Feedback
        disabled
        id={1234567}
      />
    );
    fetch.resetMocks();
  });

    it("should trigger handleSubmit asynchronously: it should call updateOtherCancelReason API first to save comments, then update state", async () => {
    fetch.mockResponseOnce(JSON.stringify({"other_reason": "I am moving to Mars."}));

    wrapper.find("button").simulate("click");

    const callAPI = await updateOtherCancelReason("google");
    expect(callAPI.other_reason).toEqual("I am moving to Mars.");

    expect(fetch.mock.calls.length).toEqual(1);

    // How to test async code here?
  });

This test above passes because I used jest-fetch-mock to simulate an API call that gives us a mock response. How can I test if updateMembershipStatus was called and has successfully updated the state to "Pending Cancellation" in ManageMembership.jsx?

Another similar post: Testing API Call in React - State not updating, no one has answered it, though.

1 Answer 1

1

I would use react-testing-library for this. If as a result of some async action some item appears on the screen, we can test it like so:

await wait(() => getByText(container, 'some-item'))
// make asserts
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.