This is part of the Semicolon&Sons Code Diary - consisting of lessons learned on the job. You're in the react category.
Last Updated: 2024-11-21
Installation
bash
npm install --save-dev @testing-library/react
// App.test.js
import { render, screen, cleanup} from '@testing-library/react';
//. You probably need an afterEach(cleanup) to reset state
afterEach(cleanup)
// In the components you should use data-testid to identify components
function Hello(props) {
if (props.name) {
return <h1 data-testid="testz">Hello, {props.name}!</h1>;
} else {
return <span data-testid="tests">Hey, stranger</span>;
}
}
// Then you can refer to these in tests with `screen.getByTestId("theid")` -- see within
test('renders hello stranger', () => {
render(<Hello/>);
// Also each test by render(<Component prop={someProp}>
// A basic assertion is to check for text content in that element
expect(screen.getByTestId("tests")).toHaveTextContent("Hey, stranger")
// FYI If you omit the `toHaveTextContent()` you get the full span or whatever
});
// If you want to test clicks
// import { fireEvent } from '@testing-library/react';
test('allows click', () => {
render(<BigItem />);
expect(screen.getByTestId("test1").textContent).toBe("Apple")
// get access the the onClick item using testid
const button = screen.getByTestId('test2')
// get call with with fireEvent
fireEvent.click(button)
expect(screen.getByTestId("test1").textContent).toBe("Poop")
});
// If you want to test typing need userEvent instead of fireEvent
import userEvent from '@testing-library/user-event'
function BigItem() {
const [text, setText] = React.useState("Apple")
return (
<div>
<h1 data-testid="test1">{text}</h1>
<button onClick={() => setText("Poop")} data-testid="test2">Dog</button>
<textarea data-testid="ta1">hi</textarea>
<p data-foo="bar">hidy hooo</p>
<summary>Stuff</summary>
</div>
)
}
test('allows text entering', () => {
render(<BigItem />);
const ta = screen.getByTestId("ta1")
expect(ta.textContent).toBe("hi")
// I use the {backspace} to get rid of the existing text there
// userEvent.type(textarea, "{backspace}{backspace}fart")
userEvent.type(ta, "{backspace}{backspace}fart")
// If you want help debugging what you see add the following
screen.debug()
expect(ta).toHaveValue("fart")
});
function Async() {
const [text, setText] = React.useState("before")
const doSomething = () => {
setTimeout(() => (
setText("after")
),500 )
}
return (
<div>
<button onClick={doSomething} data-testid="trigger"></button>
<p data-testid="result">{text}</p>
</div>
)
}
// For async stuff, use an async jest function to begin with
test.only("works for async appearing stuff", async () => {
render(<Async />)
const trigger = screen.getByTestId("trigger")
fireEvent.click(trigger)
// - then use the `findByText()` function -- do not use findByTestId (unless the test Id updates, which would be weird)
// - you will need to await on findByText.
const result = await screen.findByText("after")
// - lastly you can go the classic getByTestId stuff
expect(screen.getByTestId("result").textContent).toBe("after")
})
// If you want to use hardcore querySelect (not recommended),
test('hard to reach querySelector stuff', () => {
// You extract the contain item here
const { container } = render(<BigItem />);
// Then you can do querySelector (which is not possible elsewhere)
const hardToReach = container.querySelector('[data-foo="bar"]')
expect(hardToReach.textContent).toMatch(/hoo/)
})