React Testing Library中waitFor的container选项作用及范围失效问题
问题核心
我想了解React Testing Library里waitFor方法的container选项的作用,同时疑惑为何设置该选项后,内部调用的getByText没有被限制在指定container范围内,反而在整个document中查找导致重复元素错误。
官方文档说明
默认的
container是全局document。请确保你等待的元素是container的后代。
测试代码
/** * @jest-environment jsdom */ import { setImmediate } from 'timers'; // https://github.com/prisma/prisma/issues/8558 global.setImmediate = setImmediate; import { jest } from '@jest/globals'; import '@testing-library/jest-dom'; import { RenderResult, act, findByText, prettyDOM, render, screen, waitFor } from '@testing-library/react'; describe('RandomTest', () => { test('simpleTest__1', async () => { const renderResult = render( <div> <div id="div1"> <code>search one</code> <code>search two</code> <code>search three</code> </div> <div id="div2"> <code>find one</code> <code>find two</code> <code>find three</code> </div> </div> ); const elt_div2 = renderResult.container.querySelector<HTMLElement>(`#div2`) ?? (() => { throw new TypeError(); })(); // prettier-ignore await waitFor( () => { renderResult.getByText(/one/); }, { container: elt_div2, } ); }); })
错误信息
> tlightchat-main@0.0.0 randomUtest H:\Using\TLightChat\tlightchat-main > node --experimental-vm-modules ./node_modules/jest/bin/jest.js --runTestsByPath ./test/randomTest/Utest1.test.tsx (node:19232) ExperimentalWarning: VM Modules is an experimental feature and might change at any time (Use `node --trace-warnings ...` to show where the warning was created) FAIL test/randomTest/Utest1.test.tsx RandomTest × simpleTest__1 (1039 ms) ○ skipped simpleTest__2 ● RandomTest › simpleTest__1 Found multiple elements with the text: /one/ Here are the matching elements: <code>search one</code> <code>find one</code> (If this is intentional, then use the `*AllBy*` variant of the query (like `queryAllByText`, `getAllByText`, or `findAllByText`)). <body> <div> <div> <div id="div1"><code>search one</code><code>search two</code><code>search three</code></div> <div id="div2"><code>find one</code><code>find two</code><code>find three</code></div> </div> </div> </body> 34 | // console.log(printHtmlHltAnsi(elt_div2)); 35 | > 36 | await waitFor( | ^ 37 | () => { 38 | renderResult.getByText(/one/); 39 | }, at waitForWrapper (node_modules/.pnpm/@testing-library+dom@9.3.3/node_modules/@testing-library/dom/dist/wait-for.js:162:27) at Object.<anonymous> (test/randomTest/Utest1.test.tsx:36:11) Test Suites: 1 failed, 1 total Tests: 1 failed, 1 skipped, 2 total Snapshots: 0 total Time: 5.412 s Ran all test suites within paths "./test/randomTest/Utest1.test.tsx". ELIFECYCLE Command failed with exit code 1.
问题现象
waitFor似乎是在整个document中查找元素(导致找到重复元素触发错误),而非在指定的elt_div2内部查找(该范围内仅存在1个匹配元素)。但使用findByText(elt_div2, /one/)时,范围限制是正常的。请问这是为什么?
解答
首先明确waitFor的container选项的作用:它不是用来限制回调内部查询函数的查找范围,而是用来指定waitFor需要监听哪个容器内的DOM变化,当这个容器内发生DOM更新时,waitFor会重新执行回调函数。默认情况下它监听整个document的变化,设置为elt_div2后,只会在elt_div2内部有DOM变动时才重试回调。
你遇到的问题根源在于:renderResult.getByText这个方法是绑定到render返回的根容器(也就是整个渲染的DOM结构)上的,它的查找范围始终是这个根容器(对应整个document中的渲染内容),和waitFor的container选项无关。
要实现限制在elt_div2内部查找的需求,你需要直接在查询函数中指定查找容器:
- 使用
getByText的第一个参数传入目标容器:
await waitFor(() => { getByText(elt_div2, /one/); });
- 或者从
elt_div2上创建查询方法(比如使用within工具):
import { within } from '@testing-library/react'; // ... await waitFor(() => { within(elt_div2).getByText(/one/); });
这两种方式都会把查找范围限制在elt_div2内部,不会出现重复元素的错误。而waitFor的container选项此时可以保留,用来只监听elt_div2内的DOM变化,提升测试效率。
内容的提问来源于stack exchange,提问作者Nor.Z

