Testing Guide
This document aims to provide guidance and best practices for testing applications that use Braid.
Note that, while these opinions are based on our experience building Braid and other React applications, each team is still free to decide which testing strategy works best for them.
Since Braid relies on sku, it doesn’t require any special setup to render within Jest. However, all Braid components must be wrapped in a provider. Unit tests should use BraidTestProvider, which performs the same role as BraidProvider but smooths over a few issues that are unique to unit testing. It also sets a default theme for you since themes are largely irrelevant when unit testing. We recommend using React Testing Library. In fact, we use it internally in the Braid codebase. Unlike other testing libraries such as Enzyme, React Testing Library encourages you to write more effective unit tests by decoupling them from implementation detail. For example, the vast majority of React Testing Library’s API is focused on querying DOM elements based on outcomes a user might expect, rather than using test IDs or snapshotting raw markup. Most notably, it features role based querying that leverages the accessibility of your application for testing purposes. This has the attractive side effect of making accessibility much more prominent during the development process. For example, a typical unit test written with React Testing Library might look something like this:
import { render } from "@testing-library/react"
test("should open menu when clicked", () => {
const { getByRole } = render(<MyMenu />)
expect(getByRole("menu")).not.toBeVisible()
const menuTrigger = getByRole("button")
menuTrigger.click()
expect(getByRole("menu")).toBeVisible()
})
If you want to test specific user interactions with your component, the user-event library exposes a high-level API for simulating browser events: import { render } from "@testing-library/react"
import userEvent from "@testing-library/user-event"
test("should focus the button after pressing tab", async () => {
const user = userEvent.setup()
const { getByRole } = render(<MyComponent />)
const button = getByRole("button")
expect(button).not.toHaveFocus()
await user.tab()
expect(button).toHaveFocus()
})
We feel this approach to unit testing is more appropriate to Braid consumers for a few reasons:
Your tests will execute Braid code rather than mocking or hiding it behind shallow rendering. This is important if you want to catch issues with the integration between your codebase and Braid.
It more clearly catches issues with Braid itself. Don’t forget that we make mistakes too. If we break something, your tests should break too.
As we iterate on Braid’s internals, you won’t have snapshot tests that constantly break because of implementation details. Ideally, your test should only break if something is actually broken.
As much as possible, we suggest using role based querying to maximise accessibility. Queries based on text content, title text, labels and display values are also good options. Some Braid components allow passing HTML data attributes via the `data` prop. This allows you to pass the `data-testid` prop used by React Testing Library. However, using data props should be treated as a last resort since it’s considered implementation detail. If you’re having trouble targeting an element in a test, please reach out to us in #braid-support. For more information on this style of unit testing, check out the following resources:
The main benefit of snapshots tests is how quick they are to create, which means that you can get a lot of code coverage with little effort. However, what’s often not discussed is their relative effectiveness at catching issues compared to their maintenance cost.
Every time a component or its children change, the snapshot will change, resulting in a broken test. It’s very difficult to tell whether the component still does what it’s supposed to do just by looking at the HTML it produced. Over time, the amount of snapshots you have will grow and break more frequently. In our experience, this causes people to grow accustomed to approving screenshot diffs based on a cursory glance of the output, which means that you’re much less likely to notice when something is actually broken. This is especially relevant when using Braid because the components will change frequently, causing snapshots to break your app’s tests even though it remains perfectly functional.
When dealing with overly noisy snapshot diffs, it’s common for people to reach for shallow rendering of components as a solution. However, we believe this further reduces the effectiveness of your tests. Check out “Why I Never Use Shallow Rendering” for a great article on the issues with shallow rendering. Note: The above specifically discusses React component snapshots. Snapshot testing is a great tool when care is taken to ensure that all snapshot diffs represent meaningful differences in the behaviour of your application. In contrast, React component markup can be quite volatile and full of irrelevant markup changes.
We recommend all teams employ a small number of integration tests to ensure their core flows work as expected. Here are some great libraries to consider: