reactjsjestjsenzymemoxios

Jest How to test error while submitting data to server from form?


Consider the following code :

class Dashboard extends Component {
  state = {
    fileName: "",
    group: "",
    country: "",
    error: ""
  };
  onFormSubmit = async e => {
    e.preventDefault();
    const { group, country } = e.target;
    try {
      await axios.post("/api/dashboard", {
        name: group.value,
        country: country.value,
        image: this.state.fileName
      });
    } catch (error) {
      console.log(error.response.data.message);
      this.setState({ error: error.response.data.message });
    }
  };

  render() {
    return (
      <div>
        <form onSubmit={this.onFormSubmit}>
          <input
            type="text"
            onChange={this.onGroupChange}
            name="group"
            placeholder="Group Name"
            value={this.state.group}
          />
          {this.state.error}
          <input
            type="text"
            onChange={this.onCountryChange}
            name="country"
            placeholder="Country"
            value={this.state.country}
          />
          <ImageUpload
            fileName={this.state.fileName}
            onFileNameChange={this.onFileNameChange}
          />
          <button type="submit">Add Group</button>
        </form>
      </div>
    );
  }
}

I am trying to test the error state is set to appropriate value when post request fails. Here is the test so far:

  it("should set error state when dashboard api returns error", async () => {
    const errorMsg = "Group already exist";
    moxios.stubRequest("/api/dashboard", {
      status: 400,
      response: {
        message: errorMsg
      }
    });
    const event = {
      preventDefault: () => {},
      target: {
        group: "",
        country: ""
      }
    };
    const wrapper = shallow(<Dashboard />);
    await wrapper.find("form").simulate("submit", event);
    console.log(wrapper.state("error").value);
    expect(wrapper.state("error")).toBe(errorMsg);
  });

Solution

  • As the method onformSubmit is async, you can go with several approaches there:

    1 Await for an async method directly:

    it(
      'should set error state when dashboard api returns error',
      async () => {
        const wrapper = shallow(<Dashboard />);
        await wrapper.instance().onFormSubmit(event);
        expect(wrapper.state('error')).toBe(errorMsg);
      }
    );
    

    and separately test that the method onFormSubmit was called on submit event

    2 Use timeout with returning Promise:

    it(
      'should set error state when dashboard api returns error',
      () => {
        const wrapper = shallow(<Dashboard />);
        wrapper.find("form").simulate("submit", event);
        return new Promise(resolve => setTimeout(resolve, 1)).then(() => {
          expect(wrapper.state('error')).toBe(errorMsg);
        });
      },
      1000
    );
    

    3 await tick before checking:

    function tick() {
      return new Promise(resolve => {
        setTimeout(resolve, 1);
      });
    }
    

    ...

    it('should set error state when dashboard api returns error', async () => {
      const wrapper = shallow(<Dashboard />);
      wrapper.find('form').simulate('submit', event);
      await tick();
      expect(wrapper.state('error')).toBe(errorMsg);
    });
    

    4 Use done callback:

    it('should set error state when dashboard api returns error', done => {
      const wrapper = shallow(<Dashboard />);
      wrapper.find('form').simulate('submit', event);
      setImmediate(() => {
        expect(wrapper.state('error')).toBe(errorMsg);
        done();
      });
    });