This library has now been deprecated and moved to the official testing-library/preact repo.
- The Problem
- The Solution
- Installation
- Usage
- Example
- Hooks
- Guiding Principles
- Docs
- Contributors
- LICENSE
You want to write maintainable tests for your Preact components. As a part of this goal, you want your tests to avoid including implementation details of your components and rather focus on making your tests give you the confidence for which they are intended. As part of this, you want your testbase to be maintainable in the long run so refactors of your components (changes to implementation but not functionality) don't break your tests and slow you and your team down.
The Peact Testing Library is a very lightweight solution for testing Preact components. It provides light utility functions on top of preact/test-utils, in a way that encourages better testing practices. Its primary guiding principle is:
The more your tests resemble the way your software is used, the more confidence they can give you.
WARNING: This library has been deprecated, please install @testing-library/preact
.
This module is distributed via npm which is bundled with node and should be installed
as one of your project's devDependencies
:
npm install --save-dev preact-testing-library-next
This library has peerDependencies
listings for preact
.
π‘ You may also be interested in installing @testing-library/jest-dom
so you can use
the custom jest matchers.
π This library supports Preact X (10.x). It takes advantage of the act
test utility in
preact/test-utils
to enable both Preact Hook and Class components to be easily tested.
import { render } from 'preact-testing-library-next'
const { returns } = render(<YourComponent />, { arguments })
Arguments | Description | Default |
---|---|---|
container |
The HTML element the component is mounted to. | baseElement |
baseElement |
The root HTML element to which the container is appended to. | document.body |
queries |
Queries to bind to the baseElement. See getQueriesForElement. | null |
hydrate |
Used when the component has already been mounted and requires a rerender. Not needed for most people. The rerender function passed back to you does this already. | false |
wrapper |
A parent component to wrap YourComponent. | null |
Returns | Description |
---|---|
container |
The HTML element the component is mounted to. |
baseElement |
The root HTML element to which the container is appended to. |
debug |
Logs the baseElement using prettyDom. |
unmount |
Unmounts the component from the container. |
rerender |
Calls render again passing in the original arguments and sets hydrate to true. |
asFragment |
Returns the innerHTML of the container. |
...queries |
Returns all query functions to be used on the baseElement. If you pass in query arguments than this will be those, otherwise all. |
Unmounts the component from the container and destroys the container.
π When you import anything from the library, this automatically runs after each test. If you'd like
to disable this then set process.env.PTL_SKIP_AUTO_CLEANUP
to true when running your tests.
import { render, cleanup } from 'preact-testing-library-next'
afterEach(() => {
cleanup
}) // Default on import: runs it after each test.
render(<YourComponent />)
cleanup() // Or like this for more control.
Just a convenience export that points to preact/test-utils/act. All renders and events being fired
are wrapped in act
, so you don't really need this. It's responsible for flushing all effects and
rerenders after invoking it.
π If you'd love to learn more, checkout this explanation. Even thought it's for React, it gives you an idea of why it's needed.
Passes it to the @testing-library/dom
fireEvent. It's also wrapped in
act
so you don't need to worry about doing it.
π Keep in mind mainly when using h / Preact.createElement
that React uses a Synthetic event
system, and Preact uses the browser's native addEventListener
for event handling. This means
events like onChange
and onDoubleClick
in React, are onInput
and onDblClick
in Preact. The
double click example will give you an idea of how to test using those functions.
const cb = jest.fn();
function Counter() {
useEffect(cb);
const [count, setCount] = useState(0);
return <button onClick={() => setCount(count + 1)}>{count}</button>;
}
const { container: { firstChild: buttonNode }, } = render(<Counter />);
// Clear the first call to useEffect that the initial render triggers.
cb.mockClear();
// Fire event Option 1.
fireEvent.click(buttonNode);
// Fire event Option 2.
fireEvent(
button,
new Event('MouseEvent', {
bubbles: true,
cancelable: true,
button: 0,
});
expect(buttonNode).toHaveTextContent('1');
expect(cb).toHaveBeenCalledTimes(1);
const handler = jest.fn()
const {
container: { firstChild: input },
} = render(<input type="text" onInput={handler} />)
fireEvent.input(input, { target: { value: 'a' } })
expect(handler).toHaveBeenCalledTimes(1)
const ref = createRef()
const spy = jest.fn()
render(
h(elementType, {
onDblClick: spy,
ref,
}),
)
fireEvent['onDblClick'](ref.current)
expect(spy).toHaveBeenCalledTimes(1)
This library re-exports everything from @testing-library/dom
. See the
documentation to see what goodies you
can use. The helper functions like wait
can be particularly useful.
Note: Preact Testing Library works with both Preact Hooks and Classes. Your tests will be the same however you write your components.
function HiddenMessage({ children }) {
const [showMessage, setShowMessage] = useState(false)
return (
<div>
<label htmlFor="toggle">Show Message</label>
<input
id="toggle"
type="checkbox"
onChange={e => setShowMessage(e.target.checked)}
checked={showMessage}
/>
{showMessage ? children : null}
</div>
)
}
// NOTE: jest-dom adds handy assertions to Jest and it is recommended, but not required.
import '@testing-library/jest-dom/extend-expect'
import { h } from 'preact'
import { render, fireEvent } from 'preact-testing-library-next'
import HiddenMessage from '../hidden-message'
test('shows the children when the checkbox is checked', () => {
const testMessage = 'Test Message'
const { queryByText, getByLabelText, getByText } = render(
<HiddenMessage>{testMessage}</HiddenMessage>,
)
// query* functions will return the element or null if it cannot be found.
// get* functions will return the element or throw an error if it cannot be found.
expect(queryByText(testMessage)).toBeNull()
// The queries can accept a regex to make your selectors more resilient to content tweaks and changes.
fireEvent.click(getByLabelText(/show/i))
// .toBeInTheDocument() is an assertion that comes from jest-dom.
// Otherwise you could use .toBeDefined().
expect(getByText(testMessage)).toBeInTheDocument()
})
If you are interested in testing a custom hook, the preact-hooks-testing-library will be coming soon.
It is not recommended to test single-use custom hooks in isolation from the components where it's being used. It's better to test the component that's using the hook rather than the hook itself. The preact-hooks-testing-library will be intended to be used for reusable hooks/libraries.
We try to only expose methods and utilities that encourage you to write tests that closely resemble how your Preact components are used.
Utilities are included in this project based on the following guiding principles.
For more information checkout:
- The react-testing-library documentation.
- The react-testing-library sandbox.
- Extend Jest with custom matchers to test the state of the DOM.
- The testing library documentation.
Even though they are all React based examples, it should be close to identical in Preact. Take note of the differences between React and Preact.
Thanks goes to these people (emoji key):
Rahim Alwer π» π |
This project follows the all-contributors specification. Contributions of any kind welcome!