Published: 03 August 2020
Simple TestsLet's say we have a simple form component like this:
//myComponent.js
import React, { useState } from "react"
const MyComponent = ({ title, textBody }) => {
const [email, setEmail] = useState("")
const handleChange = e => setEmail(e.target.value)
const handleSubmit = e => {
e.preventDefault()
return email
}
return (
<form onSubmit={e => handleSubmit(e)}>
<h1>{title}</h1>
<p>{textBody}</p>
<label htmlFor="email">Type your email</label>
<input
type="text"
id="email"
onChange={e => handleChange(e)}
value={email}
/>
<button type="submit">Submit</button>
</form>
)
}
export default MyComponentLet's start writing some basic tests for this:
Each test file will have atleast 4 basic import statements:
//myComponent.test.js
// always need to import React first
import React from "react"
// all methods relating to RTL will be imported here
import { render } from "@testing-library/react"
// jes-dom allows us to have more ways of asserting against the data
import "@testing-library/jest-dom/extend-expect"
//import the component to test
import MyComp from "."
describe("My Component", () => {
it("dummy test", () => {
expect(true).toBe(true)
})
})Now, we will render our component with some basic props first:
describe("My Component", () => {
it("renders correctly", () => {
const { getByText } = render(
<MyComp title="RTL" textBody="Testing 123455" />
)
})
})Couple of things to note here:
getBy + Text option, lets use this to make some assertionsit("renders correctly", () => {
const { getByText } = render(<MyComp title="RTL" textBody="Testing 123455" />)
const customHeading = getByText("RTL")
const customText = getByText("Testing 123455")
expect(customHeading).toBeInTheDocument()
expect(customText).toBeInTheDocument()
})toBeInTheDocument is a matcher which is provided by the jest-dom import we made earlier into our test file.
When using getByText, you can also use a regular expression just in case if you don't want to type out a long string:
expect(getByText(/testing/i)).toBeInTheDocument()However, if your text is broken up into child nodes like below:
<p>To get the latest info, <a href="#">click here</a></p>Using getByText('To get the latest info, click here') won't work. You will either need to query both elements separately or use a function:
expect(
getByText((content, element) => content.includes("click here"))
).toBeInTheDocument()
// where,
// content --> represents the string encountered in the component that was rendered (use any string methods in JS)
// element --> represents the element that's currently being looped over (use any HTML methods like innerHTML)Targetting InputsTo target inputs, we have few different ways:
//get back the input to which label is attached
expect(getByLabelText("Type your email")).toBeInTheDocument()
// can also use placeholder textFiring EventsTo fire events like change and click we import the fireEvent prop and then tag on the event name:
import { render, fireEvent } from "@testing-library/react"
it("fires change event", () => {
const { getByLabelText } = render(
<MyComp title="RTL" textBody="Testing 123455" />
)
fireEvent.change(getByLabelText("Type your email"), {
target: { value: "lol" },
})
expect(getByLabelText("Type your email").value).toBe("lol")
})The second argument is usually setting up additional properties relating to the event. For the case of click:
// to fire simple left click
fireEvent.click(getByText("Submit"));
// to fire a right click
fireEvent.click(getByText("Submit"),{button:2})
})Negative AssertionsSometimes, its also handy to assert that a certain element is not in the DOM.
We can't use getBy queries since they always expect the element to be found or they throw an error.
We use queryBy instead since it will return null if nothing found like so:
expect(queryByText("an element which does not exist")).not.toBeInTheDocument()Notice, we can preface any query with .not. to indicate the inverse effect.
Targetting SVGs and ImagesImages must always an alt attribute associated with them and/or will have roles attached and so we can use getByRole or getByAltText to access them.
Mostly, SVGs will have one of the following 2 properties to be ARIA compliant:
(1) role=img --> use getByRole
(2) title --> use getByTitle
Targetting by testidIn the rare case when you have no way of accessing the element, you can add a data-testid attribute as a last resort to access it with getByTestId query.
<p data-testid="unreachable"></p>expect(getByTestId("unreachable")).toBeInTheDocument()This is a last resort because it should never happen if you follow ARIA compliant rules. You should always have some meaningful ways of targeting your nodes/element as discussed above.