您应该在每个 `test()/it()` 块中还是全局渲染组件/select 元素?

Should you render components / select elements in each `test()/it()` block or globally?

react-testing-library 中,您必须先渲染 React 组件,然后再对其元素执行一些测试。

  1. 对于同一个组件的多个测试,你应该避免 多次渲染组件?或者你必须在每个 test()/it()块?

  2. 你应该 select 每个 test()/it() 块中的组件元素(例如按钮),还是应该抬起 selection,并且 select只有一次?

  3. 对测试的执行时间有影响吗?

  4. 其中一种方法是最好的吗practice/antipattern?

  5. 为什么最后一个例子失败了?

对于基本组件我有以下测试方法:

function MyComponent() {
  return (
    <>
      <button disabled>test</button>
      <button disabled>another button</button>
    </>
  );
}

例如

describe("MyComponent", () => {
  it("renders", async () => {
    const { getByRole } = render(<MyComponent />);
    const button = getByRole("button", { name: /test/i });
    expect(button).toBeInTheDocument();
  });

  it("is disabled", async () => {
    // repetetive render and select, should be avoided or adopted?
    const { getByRole } = render(<MyComponent />);
    const button = getByRole("button", { name: /test/i });
    expect(button).toBeDisabled();
  });
});

对比

describe("MyComponent", () => {
  const { getByRole } = render(<MyComponent />);
  const button = getByRole("button", { name: /test/i });

  it("renders", async () => {
    expect(button).toBeInTheDocument();
  });

  it("is disabled", async () => {
    expect(button).toBeDisabled();
  });
});

我希望第二种方法的执行时间更快,因为组件只需要渲染一次,但我不知道如何衡量它以及它是否是反模式? 虽然它似乎更多 DRY,但如果我添加另一个 toBeInTheDocument 检查,它会失败。

为什么会这样?

describe("MyComponent", () => {
  const { getByRole } = render(<MyComponent />);
  const button = screen.getByRole("button", { name: /test/i });
  const button2 = screen.getByRole("button", { name: /another button/i });
  
  it("renders", async () => {
    expect(button).toBeInTheDocument(); //ok
  });

  it("is disabled", async () => {
    expect(button).toBeDisabled(); // ok
  });

  it("renders second button", async () => {
    expect(button2).toBeInTheDocument(); // fails: element could not be found in the document
  });
});

所以这个方法好像比较容易出错!?

每个测试都应该尽可能地原子化,这意味着它不应该使用其他测试也在使用的任何东西,并且应该 运行 具有新的状态。因此,将其与您的示例相关联,第一个将是正确的模式。

当您的测试套件包含单元测试之间的可共享状态时,例如对象或环境变量,测试套件很容易出错。原因是;如果其中一个单元测试碰巧改变了其中一个共享对象;所有其他单元测试也会受此影响,导致它们表现出不需要的行为。这可能会导致测试失败 - 代码在技术上是正确的,甚至为未来的开发人员设置地雷,其中添加正确的新测试仍然会导致失败,因此在弄清楚为什么会发生这种情况时造成严重的头痛。

此规则的唯一例外是不可变原始变量(例如 stringnumberboolean 使用 const 关键字),因为测试不会能够改变它们,它们对于存储可重用的 ID、文本等很有用。

当然,重复每个单元测试的设置会使它们变得非常笨重,这就是为什么 jest 提供 beforeEachbeforeAllafterEachafterAll 函数提取重复逻辑。但是,这会打开共享状态的漏洞,因此请务必小心并确保在开始任何测试之前刷新所有状态。 Ref.

关于最后一个示例中最后一个单元测试为何失败的问题 - 看来您正在使用 getByRole 来查找按钮文本。您应该改用 getByTextgetByRole 与您似乎没有使用的 role 属性(例如 <button role="test">test</button>)一起使用。