【问题标题】:How to test react-toastify with jest and react-testing-library如何用 jest 和 react-testing-library 测试 react-toastify
【发布时间】:2021-02-28 15:54:33
【问题描述】:

我有一个带有某种表单的屏幕,在提交时,我使用 axios 将请求发送到后端。成功收到响应后,我用 react-toastify 举杯。非常直接的屏幕。但是,当我尝试使用 jest 和 react 测试库通过集成测试来测试这种行为时,我似乎无法让 toast 出现在 DOM 上。

我有一个类似的实用程序渲染器来渲染我正在使用 toast 容器测试的组件:

import {render} from "@testing-library/react";
import React from "react";
import {ToastContainer} from "react-toastify";

export const renderWithToastify = (component) => (
  render(
    <div>
      {component}
      <ToastContainer/>
    </div>
  )
);

在测试本身中,我使用 react-testing-library 填写表单,按下提交按钮,然后等待 toast 出现。我正在使用mock service worker 来模拟响应。我确认响应返回 OK,但由于某种原因,toast 拒绝出现。我目前的测试如下:

expect(await screen.findByRole("alert")).toBeInTheDocument();

我正在寻找具有角色警报的元素。但这似乎行不通。 另外,我尝试做这样的事情:

...

beforeAll(() => {
  jest.useFakeTimers();
}

...

it("test", () => {
  ...

  act(() =>
    jest.runAllTimers();
  )
  expect(await screen.findByRole("alert")).toBeInTheDocument();
}

我是 JS 的新手,问题可能是由于 axios 和 react-toastify 的异步特性,但我不知道如何测试这种行为。我尝试了很多东西,包括模拟计时器并运行它们,模拟计时器并推进它们,不模拟它们并等待等。我什至尝试模拟 toast 的调用,但我无法让它正常工作。另外,这似乎是一个实现细节,所以我认为我不应该嘲笑它。

我认为问题是我在 axios 承诺解决后显示吐司,所以计时器不知何故感到困惑。

我试图搜索很多地方,但没有找到答案。

提前致谢。

【问题讨论】:

  • 这样你最终会在你自己的测试中测试别人的库,而它可能在测试环境中工作不正常,准备好模拟它。不知道您的组件中发生了什么,因此无法准确说明应该如何测试它。请参阅 stackoverflow.com/help/how-to-askstackoverflow.com/help/mcve 。 Promise 不是“计时器”,因此它们肯定不会被 runAllTimers 刷新。尝试flush-promises lib 再等一会儿,以防您正确地完成了其他所有操作,这足以让承诺生效。否则,使用 RTL 的 waitFor 以获得更宽松的控制流程。

标签: reactjs jestjs react-testing-library react-toastify


【解决方案1】:

谢谢@Estus Flask,但问题要愚蠢得多 :) 我必须在组件之前渲染 ToastContainer,如下所示:

import {render} from "@testing-library/react";
import React from "react";
import {ToastContainer} from "react-toastify";

export const renderWithToastify = (component) => {
  return (
    render(
      <div>
        <ToastContainer/>
        {component}
      </div>
    )
  );
};

然后,测试很简单,我只需要等待吐司的标题:

expect(await screen.findByText("alert text")).toBeInTheDocument();

findByRole 似乎由于某种原因不起作用,但我太累了,无法深入挖掘 :) 我不必使用任何假计时器或刷新承诺。显然,当您使用 await 和 finBy* 查询时,RTL 已经做到了,只是渲染顺序错误。

【讨论】:

  • 很棒的收获 :) 这很有帮助。
【解决方案2】:

我要做的是模拟 react-toastify 中的方法以窥探该方法以查看调用它的内容,而不是实际出现在屏幕上的组件:

// setupTests.js
jest.mock('react-toastify', () => {
  const actual = jest.requireActual('react-toastify');
  Object.assign(actual, {toast: jest.fn()});
  return actual;
});

然后在实际测试中:

// test.spec.js
import {toast} from 'react-toastify';

const toastCalls = []
const spy = toast.mockImplementation((...args) => {
     toastCalls.push(args)
  }
)

describe('...', () => {
  it('should ...', () => {
    // do something that calls the toast
    ...
    // then
    expect(toastCalls).toEqual(...)
   }
 }
)


另一个建议是将这个 mockImplementation 放入一个单独的辅助函数中,您可以轻松地调用它来进行所需的测试。 这是熊骨头的方法


function startMonitoring() {
  const monitor = {toast: [], log: [], api: [], navigation: []};

  toast.mockImplementation((...args) => {
    monitor.toast.push(args);
  });
  log.mockImplementation((...args) => {
    monitor.log.push(args);
  });
  api.mockImplementation((...args) => {
    monitor.api.push(args);
  });
  navigation.mockImplementation((...args) => {
    monitor.navigation.push(args);
  });
  return () => monitor;
}

it('should...', () => {
  const getSpyCalls = startMonitoring();
  // do something

  expect(getSpyCalls()).toEqual({
    toast: [...],
    log: [...],
    api: [...],
    navigation: [...]
  });
});

【讨论】:

    【解决方案3】:

    这里,解决方案是使用 getByText:

    await waitFor(() => {
      expect(screen.getByText(/Logged!/i)).toBeTruthy()
    })
    

    【讨论】:

      猜你喜欢
      • 2021-02-11
      • 2020-03-22
      • 1970-01-01
      • 2020-05-14
      • 1970-01-01
      • 1970-01-01
      • 2021-12-29
      • 2020-01-20
      相关资源
      最近更新 更多