I am using React Testing Library with jsdom and Vitest. I am trying to run multiple unit tests in one file but the screen
variable appears to have output of all render
calls, from all tests, resulting in duplicates. screen
is recommended per:
However, if I use the obsolete approach of explicit container I do not run into this issue.
How can I use screen
and run tests in parallel, making sure each one has its own isolated DOM?
This works, both tests pass (they run sequentially), but it does not use screen
:
import { render, getByText } from '@testing-library/react'
import { describe, test } from 'vitest'
describe('Parallelism test', () => {
test('Render 1', () => {
const renderResult = render(<div>foo</div>)
getByText(renderResult.container, 'foo')
})
test('Render 2', () => {
const renderResult = render(<div>foo</div>)
getByText(renderResult.container, 'foo')
})
})
But if I use screen
with sequentially running tests, I run into a failure:
import { render, screen } from '@testing-library/react'
import { describe, test } from 'vitest'
describe('Parallelism test', () => {
test('Render 1', () => {
render(<div>foo</div>)
screen.getByText('foo')
})
test('Render 2', () => {
render(<div>foo</div>)
screen.getByText('foo')
})
})
Related question:
In summary:
cleanup
invocation after each test.jsdom
via the vitest
environment, it is not possible to avoid tests in a single file reusing it. Possibly setting up jsdom
directly would solve this. Some ideas in this answer.jsdom
DOM.I believe I got it work with getByText(renderResult.container, 'foo')
because it is not correct usage. Instead, I should have used renderResult.getByText('foo')
which runs into the same problem. The renderResult.container
approach cannot find e.g. pop-ups. Furthermore, the tests in the examples I provided are run sequentially, because vitest
's --sequence.concurrent
defaults to false
.
To actually make it work at least sequentially, the cleanup must happen.
The tooling was trying to discourage me from adding any cleanup by ESLint warnings:
cleanup
doc:
This is called automatically if your testing framework (such as mocha, Jest or Jasmine) injects a global afterEach() function into the testing environment. If not, you will need to call cleanup() after each test.
However, once I ignored all the ESLint warnings and added the cleanup, tests now work when run sequentially:
import { render, screen, cleanup } from '@testing-library/react'
import { describe, test, afterEach } from 'vitest'
describe('Parallelism test', () => {
afterEach(() => {
console.log('afterEach cleanup')
cleanup()
})
test('Render 1', async () => {
console.log('r1')
render(<div>foo</div>)
screen.getByText('foo')
await delay(1000)
})
test('Render 2', async () => {
console.log('r2')
render(<div>foo</div>)
screen.getByText('foo')
await delay(1000)
})
})
async function delay(ms: number): Promise<void> {
return new Promise((resolve) => setTimeout(resolve, ms))
}
However, when run with --sequence.concurrent=true
it fails the usual way.
The good news is that tests in separate files run in parallel by default per fileParallelism
being set to true
and their environments are isolated per poolOptions.threads.isolate
being set to true
.