To increase confidence in our code and prevent it from breaking
<Counter />
<Counter />
Enzyme
<Counter />
Enzyme
<Counter />
Enzyme
<Counter />
Enzyme
😔
<Counter />
Enzyme
😔
<Counter />
Enzyme
😔
<Counter />
Enzyme
😔
<Counter />
Enzyme
😔
<Counter />
<Counter />
<Counter />
<Counter />
test("Adding a to-do", () => {
ReactDOM.render(<TodoApp />, document.body)
const addButton = document.querySelector('button')
const input = document.querySelector('input')
expect(addButton.disabled).toBe(true)
expect(input.value).toBe('')
input.value = 'Learn how to test'
addButton.click()
const newTodo = document.querySelector('ul.todo-list > li')
expect(newTodo).toBeDefined()
expect(newTodo.innerHTML).toBe('Learn how to test')
})
import { render, screen, userEvent } from "@testing-library/react";
test("Adding a to-do", () => {
render(<TodoApp />);
const addButton = screen.getByText("Add To-Do", { selector: "button" });
const input = screen.getByLabelText("To-Do text:");
expect(addButton).toBeDisabled();
expect(input).toHaveValue("");
userEvent.type(input, "Learn how to test");
userEvent.click(addButton);
expect(
screen.getByText("Learn how to test", { selector: "li" })
).toBeInTheDocument();
});
import { render, screen, userEvent } from "@testing-library/react";
test("Adding a to-do", () => {
render(<TodoApp />);
const addButton = screen.getByText("Add To-Do", { selector: "button" });
const input = screen.getByLabelText("To-Do text:");
expect(addButton).toBeDisabled();
expect(input).toHaveValue("");
userEvent.type(input, "Learn how to test");
userEvent.click(addButton);
expect(
screen.getByText("Learn how to test", { selector: "li" })
).toBeInTheDocument();
});
test("Adding a to-do", () => {
ReactDOM.render(<TodoApp />, document.body);
const addButton =
document.querySelector('button');
const input =
document.querySelector('input');
expect(addButton.disabled).toBe(true);
expect(input.value).toBe('');
input.value = 'Learn how to test';
addButton.click();
const newTodo = document.querySelector(
'ul.todo-list > li'
);
expect(newTodo).toBeInTheDocument();
expect(newTodo.innerHTML);
.toBe('Learn how to test');
});
#1: Avoid the test user
#1: Avoid the test user
function Counter() {
const [count, setCount] = useState(0);
return (
<>
<button
{/* Test for incrementing the count on button click */}
onClick={() => setCount((count) => count + 1)}
{/* Test for disabling the button once the count gets to 3 */}
className={count >= 3 ? "disabled" : ""}
>
Increment
</button>
<div>Count: {count}</div>
</>
);
}
#1: Avoid the test user
function Counter() {
const [count, setCount] = useState(0);
return (
<>
<button
{/* Test for incrementing the count on button click */}
onClick={() => setCount((count) => count + 1)}
{/* Test for disabling the button once the count gets to 3 */}
className={count >= 3 ? "disabled" : ""}
>
Increment
</button>
<div>Count: {count}</div>
</>
);
}
function Counter() {
const [clicked, setClicked] = useState(0);
return (
<>
<button
onClick={() => setCount((clicked) => clicked + 1)}
disabled={click >= 3}
>
Increment
</button>
<div>Count: {clicked}</div>
</>
);
}
#2: Readable and meaningful test code
#3: Encourages accessibility
#3: Encourages accessibility
function Dialog({ title }) {
return (
<div>
<h2>{title}</h2>
<form>
<label>Full name</label>
<input />
<input type="submit" />
</form>
</div>
)
}
#3: Encourages accessibility
function Dialog({ title }) {
return (
<div>
<h2>{title}</h2>
<form>
<label>Full name</label>
<input />
<input type="submit" />
</form>
</div>
)
}
#3: Encourages accessibility
function Dialog({ title }) {
return (
<div role="dialog">
<h2>{title}</h2>
<form>
<label>Full name</label>
<input />
<input type="submit" />
</form>
</div>
)
}
test("renders the dialog", () => {
render(<Dialog title="iRequest" />)
expect(screen.getByRole("dialog"))
.toBeInTheDocument()
})
#3: Encourages accessibility
function Dialog({ title }) {
return (
<div role="dialog">
<h2>{title}</h2>
<form>
<label>Full name</label>
<input />
<input type="submit" />
</form>
</div>
)
}
test("can submit the form values", () => {
render(<Dialog title="iRequest" />)
const fullNameInput = screen.getByLabelText("Full name")
// ...
})
#3: Encourages accessibility
function Dialog({ title }) {
return (
<div role="dialog">
<h2>{title}</h2>
<form>
<label>Full name</label>
<input />
<input type="submit" />
</form>
</div>
)
}
test("can submit the form values", () => {
render(<Dialog title="iRequest" />)
const fullNameInput = screen.getByLabelText("Full name") // 🤷♂️
// 🚨 Error: Cannot find input with label text full name
// ...
})
#3: Encourages accessibility
function Dialog({ title }) {
return (
<div role="dialog">
<h2>{title}</h2>
<form>
<label for="full-name">Full name</label>
<input id="full-name" />
<input type="submit" />
</form>
</div>
)
}
test("can submit the form values", () => {
render(<Dialog title="iRequest" />)
const fullNameInput = screen.getByLabelText("Full name") // 🤷♂️
// 🚨 Error: Cannot find input with label text full name
// ...
})
#3: Encourages accessibility
function Dialog({ title }) {
return (
<div role="dialog">
<h2>{title}</h2>
<form>
<label for="full-name">Full name</label>
<input id="full-name" />
<input type="submit" />
</form>
</div>
)
}
test("can submit the form values", () => {
render(<Dialog title="iRequest" />)
const fullNameInput = screen.getByLabelText("Full name") // 👍
// ...
})
#4: @testing-library is universal
#4: @testing-library is universal
#4: @testing-library is universal
#1: Tests are harder to write
#1: Tests are harder to write
#1a: No more shallow rendering*
*can still stub out components with Jest, but not recommended by @testing-library
#1: Tests are harder to write
#1a: No more shallow rendering*
#1b: Reliance on third-party components
*can still stub out components with Jest, but not recommended by @testing-library
#1: Tests are harder to write
#1a: No more shallow rendering*
#1b: Reliance on third-party components
#1c: Thinking about UI tests in a new way
*can still stub out components with Jest, but not recommended by @testing-library
#1: Tests are harder to write
#1a: No more shallow rendering*
#1b: Reliance on third-party components
#1c: Thinking about UI tests in a new way
#2: Tests are marginally slower
*can still stub out components with Jest, but not recommended by @testing-library
#1: Tests are harder to write
#1a: No more shallow rendering*
#1b: Reliance on third-party components
#1c: Thinking about UI tests in a new way
#2: Tests are marginally slower
#3: Accessibility learning curve
*can still stub out components with Jest, but not recommended by @testing-library
The more your tests resemble the way your software is used, the more confidence they can give you.