Как протестировать асинхронную функцию, которая делает аксиос-запрос к API, используя JEST в приложении React - PullRequest
0 голосов
/ 02 января 2019

Я пытаюсь протестировать функцию async / await, которая выполняет вызов API, используя axios для получения пользователей.Я новичок в тестировании приложений 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) ● Users ›componentDidMount› устанавливает состояниеcomponentDidMount

ожидаемое (полученное) .toEqual (ожидаемое)

Ожидаемое значение, равное: 1 полученное: 0

Пожалуйста, помогите мне разобраться в проблеме.я абсолютно новичок в тестировании реактивных приложений

Ответы [ 2 ]

0 голосов
/ 05 июня 2019

Насколько я понял, вы пытаетесь дождаться разрешения обещания, которое «скрыто» внутри вашего метода 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);
      });
    });
  });
});

Проводя некоторые исследования, я натолкнулся на эту запись : Если честно, я не знаю, хорошая ли это идея, но она работает в моих асинхронных тестах, и она избегает дополнительной ссылки на обещание в вашем компоненте. Так что не стесняйтесь попробовать это тоже!

0 голосов
/ 03 января 2019

Похоже похоже на это .

Проблема в том, что тест завершен раньше, чем async fetchUsers и затем setState (это также асинхронная операция). Чтобы исправить это, вы можете передать done обратный вызов для проверки и поместить последнее ожидание в setTimeout(fn, 0) - так что ожидаемый будет вызван после всех выполненных асинхронных операций:

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

Как уже упоминалось в комментарии, это хакерское исправление, я надеюсь, что здесь будут другие ответы с более jest способом исправить это.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...