Jest.js 如何在测试库中等待不发生的事情?

qxsslcnc  于 2022-12-08  发布在  Jest
关注(0)|答案(2)|浏览(164)

我想Assert一个条件,我知道它不会立即为真,但在异步操作后可能为真,在这种情况下测试应该失败。
假设我正在测试这个计数器组件:

function Counter() {
  const [value, setValue] = useState(1);
  function decrement() {
    if (value >= 0) { // <- off by one bug
      someAsyncAction().then(() => setValue(value - 1));
    }
  }
  return (
    <>
      Value is {value}
      <button onClick={decrement}>Decrement</button>
    </>
  );
}

我可以编写此测试来检查值不应低于零:

const button = screen.getByRole("button", { name: "Decrement" });
expect(screen.getByText("Value is 1")).toBeInTheDocument();

userEvent.click(button);
expect(await screen.findByText("Value is 0")).toBeInTheDocument();

userEvent.click(button);
// !!! wrong !!!
expect(screen.getByText("Value is 0")).toBeInTheDocument();
expect(screen.queryByText("Value is -1")).not.toBeInTheDocument();
// !!! wrong !!!

但是最后两个Assert * 将始终通过 *,即使组件有一个bug,这意味着它将异步更新以显示“Value is-1”。
处理这种情况的建议方法是什么?

62lalag4

62lalag41#

我想到的最好的办法是:

await expect(
  screen.findByText("Value is -1", {}, { timeout: 100 })
).rejects.toThrow();

它尝试查找文本,等待超时(从默认的1秒减少到100毫秒,以加快测试速度),然后拒绝expect捕获的文本。如果文本 * 是 * 存在,则findByText调用将解析,因此expect将拒绝,测试将失败(不要忘记awaitexpect)。
此模式可以使用waitFor扩展到其他Assert:

await expect(
  waitFor(
    () => { expect(screen.getByRole('input')).toHaveValue(-1); },
    { timeout: 100 }
  )
).rejects.toThrow();

虽然这是可行的,但它有点复杂,特别是waitFor表单。我觉得这仍然可以改进,所以如果你有任何建议,请插话。

eaf3rand

eaf3rand2#

采用Tamlyn's excellent self-answer中提出的概念,我将其外推到一个名为verifyNeverOccurs的实用程序中,该实用程序与Testing Library的waitFor具有相同的签名,但只有在您发送给它的回调Assert通过时才会失败:

import { waitFor, waitForOptions } from '@testing-library/react';

/**
 * Inverse of RTL's `waitFor`; used to verify that a thing does *not* occur. 
 * Useful for situations in which verifying that some effect did occur would
 * require using `await waitFor()` and you need to test that the effect does
 * not occur. Like `waitFor`, it must be `await`ed.
 * @param {function} negativeAssertionFn - a callback function that expects a thing you do _not_ expect will occur
 * @param {Object} options - options object with same shape as `waitFor`'s options argument (ultimately just passed through to `waitFor`)
 * @return {void}
 */

const verifyNeverOccurs = async (negativeAssertionFn: () => unknown, options?: waitForOptions) => {
  await expect(
    waitFor(negativeAssertionFn, options),
  ).rejects.toThrow();
};

export default verifyNeverOccurs;

(This是在TypeScript中,但如果您希望在普通JS中使用,则可以随时剥离类型)。
示例用法:

// fails if `element` ever contains the text "oh no"
verifyNeverOccurs(() => expect(element).toHaveTextContent('oh no'));

相关问题