Jest - проверка локального хранилища вызывается в асинхронной функции, которая проверяется - PullRequest
0 голосов
/ 11 декабря 2018

У меня есть вызов API в компоненте реагирования, который выглядит следующим образом.

login = () => {
   // <--- If I set the localStorage on this line the test passes.
   apiRequest.then(res => {
      localStorage.setItem('token', res.token);
   });
}

Чтобы проверить это, я издевался над вызовом API.Я хочу проверить, называется ли локальное хранилище, поэтому также смоделировал localStorage, так как localStorage установлен в вызове mocked api, который никогда не вызывается.Мой тестовый код ниже.Кто-нибудь знает, как я могу проверить, что локальное хранилище установлено в поддельном вызове.Я подтвердил, что если я перемещаю localStorage за пределы apiRequest, он работает, поэтому он корректно проверяется, проблема определенно в том, что он находится в apiRequest.

// This mocks out the api call
jest.mock('./api', () => {
    return {
        apiRequest: jest.fn(
           () =>
             new Promise(resolve => {
                resolve();
             })
        ),
    };
});


const localStorageMock = (() => {
   const store = {};
   return {
      setItem: jest.fn((key, value) => {
          store[key] = value.toString();
      })
   }
})();

Object.defineProperty(window, 'localStorage', {
   value: localStorageMock
});

it('sets a token in local storage', () => {
    const { getByText } = render(<Login />);
    const loginButton = getByText(/login/i);
    // This passes
    expect(apiRequest).toBeCalledTimes(1);
    // This never gets called as it is being called in the apiRequest
    expect(localStorage.setItem).toBeCalledWith('token', '1234');
});

Если что-то неясно, дайте мне знать, и я предоставлю более подробную информацию.

Ответы [ 2 ]

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

localStorage.setItem вызывается асинхронно через .then

login = () => {
   apiRequest.then(res => {
      localStorage.setItem('token', res.token);
   });
}

Так что насмешка не может помочь с асинхронным потоком.Эта небольшая часть

   .then(res => {
      localStorage.setItem('token', res.token);
   }

просто помещается в конец очереди (она называется очередь микрозадач , если вас интересуют подробности)

Итак, ваш тестовый код завершени только после этого выполняется эта небольшая микрозадача.

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

Вы можете использовать setTimeout (макрозадачу) для этого:

it('sets a token in local storage', done => {
    const { getByText } = renderLogin();
    const loginButton = getByText(/login/i);
    expect(apiRequest).toBeCalledTimes(1);
    setTimeout(() => {
        // runs after then(....setItem) has been called
        expect(localStorage.setItem).toBeCalledWith('token');
        done();
    }, 0);
});

или создайте микрозадачу с Promise / async / await:

it('sets a token in local storage', async () => {
    const { getByText } = renderLogin();
    const loginButton = getByText(/login/i);
    expect(apiRequest).toBeCalledTimes(1);
    await Promise.resolve(); // everything below goes into separate microtask
    expect(localStorage.setItem).toBeCalledWith('token');
});

[UPD] интересная вещь о await, которую можно использовать со всем остальным, не только с Promise.И это может работать как Promise.resolve(<some value here>).Так что в вашем случае

it('sets a token in local storage', async () => {
    const { getByText } = renderLogin();
    const loginButton = getByText(/login/i);
    await expect(apiRequest).toBeCalledTimes(1);
    expect(localStorage.setItem).toBeCalledWith('token');
});

также будет работать.Но я полагаю, что это выглядит запутанным («ваааат? .toHaveBeenCalled() возвращает обещание по-настоящему ?!») и подозрительным (это волшебство! Мне не разрешают это трогать!).Так что лучше выбрать версию с простой «отсрочкой»

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

Распространенная проблема, когда вы пытаетесь протестировать асинхронный код, это то, что вам также нужны асинхронные тесты, попробуйте от await до apiRequest, которые необходимо решить, а затем проверьте, было ли вызвано локальное хранилище.

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