react-testing-library
react-testing-library
to render and test React
Components
react-testing-library
react-testing-library
history
object with a bad entry to test
the react-router no-match
route
document.createElement('div')
to create a mount point, and then
evaluate that div
's content using query
selectors.
HTMLInputElement.type
returns the type of the input.
Node.textContent
returns the text inside a DOM node and
its descendants.
babel-jest
manually if using Babel 7 as it's
already installed when Jest is installed
(see npx jest --showConfig
). To correctly transpile JSX
you'll need @babel/core
and [email protected]
. Tests will transpile JSX without any
additional libraries or config.
jest-dom
is a library that does the heavy lifting
for us when it comes to assertions on
the DOM. We have two mechanisms to
extend Jest's expect
using jest-dom
's matchers:
jest-dom
, and then use expect.extend(// object with
names of matchers to
extend)
jest-dom/extend-expect
to make all matchers available
in the file
setupTestFrameworkScriptFile
to extend expect
for all files.
textContent
doesn't give us any confidence in
the label actually performing its job.
If the id
attribute is not properly configured,
users will not get the benefit of the
label.
dom-testing-library
to write tests that can better describe
how UIs work and do the heavy lifting
for us.
queries
export from dom-testing-library
has a number of methods on it, one of
which is .getByLabelText
which returns an HTMLInputElement
based on the id
attribute of the label
matching the text passed into the
function.
label
s text, so we can use an case
insensitive regex to select the
element.
dom-testing-library
has a convenient export that allows us
to retrieve all the query methods that
it exports but for a specific
element.
react-testing-library
to render and test React
Components
react-testing-library
does!
react-testing-library
's render
function works differently from our
naive implementation.
body
element so that we have full access to
all DOM eventing.
unmount
exported from calling render
to unmount our component once a
test has run
cleanup
to manually clean up the DOM
after a test has run
cleanup
to Jest's afterEach
hook to automatically clean up
the DOM after each test
runs
react-testing-library/cleanup-after-each
which will run Jest's afterEach
hook with cleanup
for us
react-testing-library
s render
method returns debug
function which does exactly this.
debug()
we'll have a pretty-printed result
of our component printed to the console
in our tests. Passing in a queried
element will render only that
element.
react-testing-library
s fireEvent
export.
fireEvent
has a number of event methods on it,
such as .change
, .click
, etc. that can be dispatched on an
element. A second parameter passes
values through to the event
handler.
container
property exported by render
, and asserting it has text using .toHaveTextContent
.
getByText
query returned by render
to validate if an element exists. getByText
throws an error if an element can't
be found. This is true for all the get
methods returned by render
.
getByTestId
method that render
exports along with a data-testid
attribute on the element whose content
you want to validate exists.
react-testing-library
rerender
method returned by render
.
react-testing-library
.get
methods returned from render
throw errors if an element can't be
found in the DOM. This will result in
tests erroring, even if we're
testing for the non-existence of
elements.
.query
methods that render
returns that return null
instead of throwing an error.
expect(queryByX()).toBeFalsy()
to assert that the elements don't
exist.
__tests__/form.test.js
jest-axe
we can assert the accessibility of our
components.
axe
export from jest-axe
is accepts a DOM node, and is async, so
we need tests containing a11y assertions
to be async / await
.
jest-axe
also exports a toHaveNoViolations
matcher which can either be made
avilable in assertions using expect.extend(toHaveNoViolations)
, or can be automatically extended on expect
by importing jest-axe/extend-expect
.
__tests__/greeting-loader-mock-01.test.js
async/await
we need to
wait
from react-testing-libary
to assert async
responses
jest.mock
and jest.fn
so that we can assert calls on
the function
wait
expects
__tests__/greeting-loader-mock-02.test.js
__tests__/hidden-message.test.js
__tests__/error-boundary.test.js
error-boundary.js
component we need to do a few
things:
console.error
console.error
is being called the correct
number of times, since we may be
losing important information by
squashing the output
reportError
API call we mock is being called
the correct number of times, and
with the correct
parameters
renderError
is not called, and that our
component does not output the
error text
console.error
we need to use beforeEach
to mock it out before each test runs,
and restore it after each test using afterEach
. To create the mock we need to use
Jest's .spyOn
method along with .mockImplementation
:
rerender
with an error, at which point we can
assert that console.error
and reportError
were called. We need to assert that renderError
was not only called, but called with the
parameters that we expect, using .toHaveBeenCalledWith
.
rerender
our component without throwing an error.
At this stage our component's state
is still unchanged, so it's still
showing that there was an error.
__tests__/post-editor-01-markup.test.js
.getBy
functions that render
exports allows one to assert that
components are rendering without an
explicit expect
. This is because if the components
didn't exist, the .getBy
functions would throw errors.
__tests__/post-editor-02-markup.test.js
__tests__/post-editor-03-markup.test.js
event
:
__tests__/post-editor-04-markup.test.js
Redirect
from react-router
we need to:
Redirect
from react-router
so that we can assert on
it
Redirect
as MockRedirect
so that we can easily see that
we're asserting on a mocked
function
jest.mock
to mock out react-router
and specifically Redirect
wait
from react-testing-library
to assert that MockRedirect
was indeed called
react-testing-library
's wait
executes each assertion every 15ms for
4s for each assertion. If there are 4
assertions inside wait
, and 1 of them fails, we will only
know after 16s.
wait
as low as possible to ensure faster test
runs.
__tests__/post-editor-05-markup.test.js
__tests__/post-editor-06-markup.test.js
test-data-bot
can be used to generate data to help
indicate what is important to test, vs
what can be created on the fly.
__tests__/post-editor-07-markup.test.js
react-testing-library
's waitForElement
does exactly this. We use await
to wait for waitForElement
to retreive an element we request inside
its callback.
mockSavePost
implementation, but this would cause
other tests to then fail.
mockSavePost
reject the promise, we use Jest's mockFn.mockRejectValueOnce
and provide a value that it will reject
with.
mockFn.mockReturnThis()
mockFn.mockReturnValue(value)
mockFn.mockReturnValueOnce(value)
mockFn.mockResolvedValue(value)
mockFn.mockResolvedValueOnce(value)
mockFn.mockRejectedValue(value)
mockFn.mockRejectedValueOnce(value)
__tests__/post-editor-08-markup.test.js
__tests__/main-01.test.js
-01
react-router-dom
require context in order for them to
fucntion. This is provided through a Router
in applications, so we need to provide
this to individual components in order
to test them.
Router
directly and passing in history
using the history
module's createMemoryHistory
function, and the other by using MemoryRouter
and providing initialEntries
as a prop.
history
object with a bad entry to test the
react-router no-match route
__tests__/main-01.test.js
react-router
one can either set initialEntries
in createMemoryHistory
to an invalid path, or directly as a
prop on MemoryRouter
.
__tests__/main-02.test.js
Main
inside a Router
component configured with its own
history in main-01.test.js
.
Link
and Route
components we'd need to duplicate
the effort again.
render
function that does the work for us. By
allowing that function to accept any
React component we can reuse the new render
function anywhere.
render
can also be moved to a test utils file
that can be imported into tests and used
ad-hoc where it makes sense.
__tests__/redux-app-01.test.js
react-redux
s connect
function, we need to create a store
using redux
s createStore
function, and wrap our component in react-redux
s Provider
component providing it with the store we
created.
__tests__/redux-app-02.test.js
createStore
as a second parameter.
__tests__/redux-app-03.test.js
store
and initial state.
store
we create one using the full reducer
from the app.
__tests__/toggle.test.js
children
function that will be passed to
the render prop that receives
values from the render
prop
children
function.
__tests__/modal.test.js
react-testing-library
s within
and pass in the node we want queries to
be scoped to.
within
returns the same .query
and .get
methods that render does, but scoped to
the node it is called with.
setTimeout
and setInterval
, we don't want to be subject to
the length of time those tiemrs take to
execute.
jest.useFakeTimers()
.
jest.runAllTimers()
- runs all pending timers. If,
however, we have a recursive setTimeout
calling itself, we'll end
up in a loop
jest.runOnlyPendingTimers()
- this will run only the pending
timers, and no other timers.
This can be used for timers that
call themselves
recursively
countdown.test.js
is evaluated for each of the
following.
clearInterval
in componentWillUnmount
jest.useFakeTimers()
jest.runOnlyPendingTimers()
setState
not being called, because our assertion
runs before the last setState
is actually called.
clearInterval
has been called, however, reveals that
we're not clearning any timers - a
clear indication of a memory leak.
clearInterval
in componentWillUnmount
jest.useFakeTimers()
jest.runOnlyPendingTimers()
clearInterval
is at least showing that it's been
called, so that's one step in the
right direction.
clearInterval
in componentWillUnmount
jest.useFakeTimers()
jest.runOnlyPendingTimers()
clearInterval
assertion is still failing.
clearInterval
in componentWillUnmount
jest.useFakeTimers()
jest.runOnlyPendingTimers()
jest.useFakeTimers()
clearInterval
in componentWillUnmount
jest.useFakeTimers()
jest.runOnlyPendingTimers()
clearInterval
in componentWillUnmount
jest.useFakeTimers()
jest.runOnlyPendingTimers()
jest.useFakeTimers()
clearInterval
in componentWillUnmount
jest.useFakeTimers()
jest.runOnlyPendingTimers()
jest.useFakeTimers()
that setInterval
is mocked, and that with jest.runOnlyPendingTimers()
that our assertion will only run once
all remaining timers have run.