Как проверить вызов API в компоненте реакции и ожидать изменения вида после успешного вызова API? - PullRequest
0 голосов
/ 16 декабря 2018

У меня есть простой компонент реагирования только с одной кнопкой, когда эта кнопка нажата, она выполняет вызов API, используя fetch, после успешного вызова вызывает setState для обновления компонента.

в моей кнопке my.jsx файл

import React from "react";

export default class MyButton extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            user: null
        }
        this.getUser = this.getUser.bind(this);
    }

    async getUser() {
        try {
            const res = await fetch("http://localhost:3000/users");
            if (res.status >= 400)
                throw new Error("something went wrong");

            const user = await res.json();
            this.setState({ user });

        } catch (err) {
            console.error(err);
        }


    }

    render() {

        return (
            <div>
                <button onClick={this.getUser}>Click Me</button>
                {this.state.user ? <p>got user</p> : null}
            </div>
        )
    }


}

в моем тестовом файле

import React from "react";
import { shallow, Mount } from "enzyme";
import MyButton from "../my-button";


beforeAll(() => {
    global.fetch = jest.fn();
});

it("must test the button click", (done) => {

    fetch.mockImplementation(() => {
        return Promise.resolve({
            status: 200,
            json: () => {
                return Promise.resolve({ name: "Manas", userId: 2 });
            }
        });
    });
    const wrapper = shallow(<MyButton />);


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

    //here using setTimeout to delay the find call, How to avoid using setTimeout
    setTimeout(() => {
        wrapper.update();
        expect(wrapper.find("p").length).toBe(1);
        fetch.mockClear();
        done();
    }, 1000)
})

Я использую setTime out для задержки ожидаемого вызова, чтобы избежать использования setTimeout, поскольку это не эффективный способ тестирования.

мой тест не пройден, если я не использую setTimeout

 src/App.test.js
 FAIL  src/components/__test__/my-button.test.js
  ● must test the button click

    expect(received).toBe(expected) // Object.is equality

    Expected: 1
    Received: 0

      26 |     // setTimeout(() => {
      27 |     wrapper.update();
    > 28 |     expect(wrapper.find("p").length).toBe(1);
         |                                      ^
      29 |     fetch.mockClear();
      30 |     done();
      31 |     // }, 1000)

      at Object.toBe (src/components/__test__/my-button.test.js:28:38)

Test Suites: 1 failed, 1 passed, 2 total
Tests:       1 failed, 1 passed, 2 total
Snapshots:   0 total

Ответы [ 2 ]

0 голосов
/ 16 декабря 2018

Если у вас есть асинхронные вызовы во время выполнения теста, вы должны выполнить свои утверждения / ожидания в конце цикла событий.

it('must test the button click', done => {
  fetch.mockImplementation(() => {
    return Promise.resolve({
      status: 200,
      json: () => {
        return Promise.resolve({ name: 'Manas', userId: 2 });
      }
    });
  });
  const wrapper = shallow(<MyButton />);

  wrapper.find('button').simulate('click'); // async invocation

  // wait till async action is done
  new Promise((resolve, reject) => {
    setImmediate(() => {
      resolve();
    }, 0);
  }).then(() => {
    wrapper.update(); // you probably won't need this line
    expect(wrapper.find('p').length).toBe(1);
    fetch.mockClear();
    done();
  });
});

Обычно я записываю это как метод util во всех моихпроекты.

// test-helper.js
export const waitForAsyncActionsToFinish = () => {
  return new Promise((resolve, reject) => {
    setImmediate(() => {
      resolve();
    }, 0);
  });
};

it('test something', (done) => {
  // mock some async actions
  const wrapper = shallow(<Component />);

  // componentDidMount async actions
  waitForAsyncActionsToFinish().then(() => {
    wrapper.find('.element').simulate('click');

    // onClick async actions - you have to wait again
    waitForAsyncActionsToFinish().then(() => {
      expect(wrapper.state.key).toEqual('val');
      done();
    });
  });
});
0 голосов
/ 16 декабря 2018

Использование settimeout обеспечивает соблюдение этого порядка для ожиданий теста.

getUsertestExpectations

С существующей реализацией MyButton не существует прямого способа достичьтот.getUser необходимо извлечь и передать в качестве реквизита MyButton, чтобы можно было лучше контролировать цепочку обещаний , то есть ожидания теста цепочки для getUser

Образец

getUser().then(testExpectations)

На первом этапе рефакторинга вызовите getUser в кнопке onClick вместо симуляции вызова ShallowWrapper вашего компонента.

Это то, что делает имитация, но возвращает экземпляр оболочки.Вы не хотите этого;вы хотите, чтобы обещание вернулось после вызова getUser, чтобы вы могли подключиться к нему.

it("must test the button click", (done) => {
  fetch.mockImplementation(() => {
    return Promise.resolve({
      status: 200,
      json: () => Promise.resolve({ name: "Manas", userId: 2 })
    });
  });

  const wrapper = shallow(<MyButton />);
  const button = wrapper.find("button");
  const onClick = button.prop('onClick');

  onClick().then(() => {
    wrapper.update();
    expect(wrapper.find("p").length).toBe(1);
    fetch.mockClear();
    done();
  })
})

Следующим шагом рефакторинга будет перенаправление getUser в качестве свойства на MyButton.В этом может не быть необходимости, если вы обнаружите, что MyButton всегда будет использовать эту конкретную реализацию для своего обработчика событий щелчка.

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