act
lets you wrap rendering and updates in tests to ensure changes are applied to the DOM before making assertions.
act(updateFn)
To prepare a component for assertions, wrap the code rendering it and performing updates inside an act()
call. This makes your test run closer to how React works in the browser.
Reference
act(updateFn)
When writing UI tests, tasks like rendering, user events, or data fetching can be considered as “units” of interaction with a user interface. React provides a helper called act()
that makes sure all updates related to these “units” have been processed and applied to the DOM before you make any assertions.
The name act
comes from the Arrange-Act-Assert pattern.
it ('renders with button disabled', () => {
act(() => {
root.render(<TestComponent />)
});
expect(container.querySelector('button')).toBeDisabled();
});
Parameters
updateFn
: A function wrapping renders or interactions for components being tested. Any updates triggered within theupdateFn
are added to an internal act queue, which are then flushed together to process and apply any changes to the DOM.
Returns
act
does not return anything.
Usage
For example, let’s say we have this Counter
component:
function Counter() {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(prev => prev + 1);
}
useEffect(() => {
document.title = `You clicked ${this.state.count} times`;
}, [count]);
return (
<div>
<p>You clicked {this.state.count} times</p>
<button onClick={this.handleClick}>
Click me
</button>
</div>
)
}
Here is how we can test it:
import React from 'react';
import ReactDOM from 'react-dom/client';
import Counter from './Counter';
let container;
beforeEach(() => {
container = document.createElement('div');
document.body.appendChild(container);
});
afterEach(() => {
document.body.removeChild(container);
container = null;
});
it('can render and update a counter', () => {
// Test first render and effect
React.act(() => {
ReactDOM.createRoot(container).render(<Counter />);
});
const button = container.querySelector('button');
const label = container.querySelector('p');
expect(label.textContent).toBe('You clicked 0 times');
expect(document.title).toBe('You clicked 0 times');
// Test second render and effect
React.act(() => {
button.dispatchEvent(new MouseEvent('click', {bubbles: true}));
});
expect(label.textContent).toBe('You clicked 1 times');
expect(document.title).toBe('You clicked 1 times');
});