如何在React应用程序中使用JEST测试向api发出axios请求的异步函数

时间:2019-01-02 16:40:13

标签: reactjs async-await jestjs enzyme

我正在尝试测试async / await函数,该函数使用axios进行api调用以获取用户。我是刚开始测试React应用程序并使用JEST(第一次尝试),因此无法运行测试。

我尝试在JEST中使用模拟功能。我的代码如下:

  // component --users

  export default class Users extends React.Component {
      constructor(props) {
        super(props);
        this.state = {};
        this.getUsers = this.getUsers.bind(this);
       }

  /*does an api request to get the users*/
  /*async await used to handle the asynchronous behaviour*/
  async getUsers() {
    // Promise is resolved and value is inside of the resp const.
     try {
      const resp = await axios.get(
        "https://jsonplaceholder.typicode.com/users"
      );

      if (resp.status === 200) {
        const users = resp.data;
        /* mapped user data to just get id and username*/
        const userdata = users.map(user => {
          var userObj = {};
          userObj["id"] = user.id;
          userObj["username"] = user.username;
          return userObj;
        });
        return userdata;
      }
    } catch (err) {
      console.error(err);
    }
  }


  componentDidMount() {
    this.getUsers().then(users => this.setState({ users: users }));
  }

  /*****************************************************************/
  //props(userid ,username) are passed so that api call is made for
  //getting posts of s psrticular user .
  /*****************************************************************/
  render() {
    if (!this.state.users) {
      return (
        <div className="usersWrapper">
          <img className="loading" src="/loading.gif" alt="Loading.." />
        </div>
      );
    }

    return (
      <div className="usersWrapper">

        {this.state.users.map(user => (
          <div key={user.id}>
            <Posts username={user.username} userid={user.id} />
          </div>
        ))}
      </div>
    );
  }
}


//axios.js-mockaxios
export default {
    get: jest.fn(() => Promise.resolve({ data: {} }))
};

//users.test.js


describe("Users", () => {
  describe("componentDidMount", () => {
    it("sets the state componentDidMount", async () => {
      mockAxios.get.mockImplementationOnce(
        () =>
          Promise.resolve({
            users: [
              {
                id: 1,
                username: "Bret"
              }
            ]
          }) //promise
      );

      const renderedComponent = await shallow(<Users />);
      await renderedComponent.update();
      expect(renderedComponent.find("users").length).toEqual(1);
    });
  });
});

测试失败-

  

FAIL src / components / users.test.js(7.437s)●用户›   componentDidMount›设置状态componentDidMount

     

期望(已接收).toEqual(预期)

     

期望值等于:      1个   收到:      0

请帮助我找出问题所在。我对测试reactapps完全陌生

2 个答案:

答案 0 :(得分:0)

看起来像similar to this one

问题是测试是否要比异步fetchUsers和setState更早完成(这也是异步操作)。要解决此问题,您可以通过done回调进行测试,并将最后的期望值放入setTimeout(fn, 0)-这样,将在所有异步操作完成后调用期望值:

it("sets the state componentDidMount", (done) => {
  ...
  setTimeout(() => {
    expect(renderedComponent.find("users").length).toEqual(1);
    done();
  }, 0);
});

正如评论中提到的那样,它是很棘手的修复程序,我希望这里是其他答案,并提供更多jest的修复方法。

答案 1 :(得分:0)

据我了解,您正在尝试做的是等待对Promise的解析,该Promise被“隐藏”在componentDidMount()方法内部。

expect(renderedComponent.find("users").length).toEqual(1);
在这种情况下,

在我看来不起作用,因为find()试图选择DOM元素。如果要检查状态,则需要使用state()

expect(renderedComponent.state("users").length).toEqual(1);

仍然,您将必须找到正确的方法来等待诺言的解决:

要参考之前的文章和评论,我看不到将async / await与任何一种酶方法(更新,实例或其他方法)结合使用不会产生任何效果。酶文档也没有朝着该方向提供任何提示,因为答应解决不是酶的工作。

前进的唯一健壮,干净且(jest-default)的方式是各种方法的混合。这是您的代码略有更改的地方:

// component --users

export default class Users extends React.Component {
  constructor(props) {
    super(props);
    this.state = {};
    this.getUsers = this.getUsers.bind(this);
  }

  /*does an api request to get the users*/

  /*async await used to handle the asynchronous behaviour*/
  async getUsers() {
    // Promise is resolved and value is inside of the resp const.
    try {
      const resp = await axios.get(
        "https://jsonplaceholder.typicode.com/users"
      );

      if (resp.status === 200) {
        const users = resp.data;
        /* mapped user data to just get id and username*/
        const userdata = users.map(user => {
          var userObj = {};
          userObj["id"] = user.id;
          userObj["username"] = user.username;
          return userObj;
        });
        return userdata;
      }
    } catch (err) {
      console.error(err);
    }
  }


  componentDidMount() {
    // store the promise in a new field, not the state since setState will trigger a rerender
    this.loadingPromise = this.getUsers().then(users => this.setState({users: users}));
  }

  /*****************************************************************/
  //props(userid ,username) are passed so that api call is made for
  //getting posts of s psrticular user .
  /*****************************************************************/
  render() {
    if (!this.state.users) {
      return (
        <div className="usersWrapper">
          <img className="loading" src="/loading.gif" alt="Loading.."/>
        </div>
      );
    }

    return (
      <div className="usersWrapper">

        {this.state.users.map(user => (
          <div key={user.id}>
            <Posts username={user.username} userid={user.id}/>
          </div>
        ))}
      </div>
    );
  }
}


//axios.js-mockaxios
export default {
  get: jest.fn(() => Promise.resolve({data: {}}))
};

//users.test.js
describe("Users", () => {
  describe("componentDidMount", () => {
    it("sets the state componentDidMount",() => {
      mockAxios.get.mockImplementationOnce(
        () =>
          Promise.resolve({
            users: [
              {
                id: 1,
                username: "Bret"
              }
            ]
          }) //promise
      );

      const renderedComponent = shallow(<Users/>);
      //Jest will wait for the returned promise to resolve.
      return renderedComponent.instance().loadingPromise.then(() => {
        expect(renderedComponent.state("users").length).toEqual(1);
      });
    });
  });
});

在做一些研究时,我遇到了这个post:老实说,我不知道这是否是一个好主意,但它在我的异步测试中有效,并且避免了对promise的其他引用在您的组件中。所以也可以尝试一下!