Table of Contents
- Takeaways
- 1. Use
dom-testing-library
with React - 2. Use
dom-testing-library
with Preact - 3. Use
dom-testing-library
with jQuery - 4. Use
dom-testing-library
with Dojo - 5. Use
dom-testing-library
with HyperApp - 6. Use
dom-testing-library
with AngularJs - 7. Use
dom-testing-library
with Angular - 8. Use
dom-testing-library
with Vue - 9. Use
dom-testing-library
with Mithril - 10. Use
dom-testing-library
with Svelte - 11. Use
dom-testing-library
withfrom-html
Testing ui components can be broken down to the following steps:
- create a
render
function to abstract the repetitive work of rendering the component you will be testing - identify how to render the component to HTML
- append that HTML to a container you have control of
- use
dom-testing-library
to get all the different functions for getting and querying elements in your container usinggetQueriesForElement
- in your test, render the component using your
render
function - if the library renders asynchronously, make your test async, make your
render
function async, andawait
when calling yourrender
function - if the library you are using renders updates asynchronously when events
trigger updates, use
await
to wait for the update before asserting. Most async updates occur at the end of the current event loop, but not all. If you know updates happen via the event loop, you can simply useawait
anddom-testing-library
swait
. If they are non-deterministically updated, e.g. not using the event loop, you'll need to assert insidewait
s callback so that Jest keeps asserting until either the UI is updated or the test fails.
$ npx jest react
To manually mount a React component, we need a few things:
- a
render
function that will mount our component - we need somewhere to mount our component, so our render function creates a
container element using
document.createElement
. - using
ReactDOM.render
we render components into that container - React uses
body
to append all event handlers. We need to account for that, so we append our container tobody
usingdocument.body.appendChild
- query functions to get elements in the document. We use the exported
getQueriesForElement
fromdom-testing-library
to get all the different queries, and return them in our ownrender
function - we also return our container so that we can inspect it directly
- because we've mounted our component using React, and appended it to the DOM,
we need to do some cleanup. We return a
cleanup
function in render that first unmounts our component usingReactDOM.unmountComponentAtNode
which accepts the container we mounted our component at. We also usedocument.body.removeChild
to remove our component from the DOM.cleanup
will need to be used after every test to ensure that subsequent tests do not contain previously rendered components.
$ npx jest preact
To manually render Preact components:
- we need a
render
function - in that render function we need a container on which we can mount our
components, so we create one with
document.createElement('div')
- we then use
Preact.render(ui, container)
to render our component on that container - we then need query methods to assert on rendered elements, so we export the
result of applying
container
todom-testing-library
sgetQueriesForElement
function, spreading it - we return the container, too
Preact doesn't render synchronously, as React does. When firing events that change state, Preact will only render those updates at the next tick of the event loop.
We have 2 strategies available to us handle this:
- wrap our assertion in a
setTimeout
with a delay of 0 - use
async await
withdom-testing-library
swait
function to wait for the next tick of the event loop before asserting
// wait for render updates using setTimeout
fireEvent.click(button)
setTimeout(() => {
expect(something).toBe(true)
})
// wait for render updates using async await
test('something', async () => {
// ...
fireEvent.click(button)
await wait()
expect(something).toBe(true)
})
Using async await
is cleaner, and we can clean things up further by extending
dom-testing-library
s fireEvent
by making every method async
:
Instead of firing an event, waiting, and then asserting, we have now combined the event firing and waiting into one function:
test('something', async () => {
// ...
await fireEventAsync.click(button)
expect(something).toBe(true)
})
$ npx jest jquery
To test jquery
components we use getQueriesForElement
on a section of DOM
that we can create using jQuery.
Unlikely to ever use Dojo / not interested in using it.
$ npx jest hyperapp
As with Preact, HyperApp updates state only at the end of each tick. In addition to state, HyperApp does this when rendering, too.
To account for this, we need to make our custom render
function async, and
await
the result of rendering before making any assertions or queries.
Unlikely to ever use AngularJs / not interested in using it.
Unlikely to ever use Angular / not interested in using it.
$ npx jest vue
Vue is similar to Preact as it updates state at the end of each event loop. We
need to use async / await
when asserting on events that result in state being
updated.
$ npx jest mithril
Mithril differs from other libraries that are asynchronous. Mithril doesn't update the DOM in a deterministic amount of time, such as at the end of the event loop.
To account for this, we need to use a callback inside dom-testing-library
s
wait
function. This will continuously call the assertion within a 4000ms
window until the assertion passes, otherwise failing the test.
$ npx jest svelte
Svelte operates synchronously, as does React, so no need for async / await
for
state updates.
$ npx jest from-html
from-html
leaves it up to the user to manage state etc. Events are handled
synchronously, so there's no need for async / await
.