React Testing Crash Course
Abbreviations
*[E2E]: End-to-End
Introduction
Learn about testing in React including unit, e2e and integration testing with React Testing Library, Jest & Cypress
Why Should You Test?
Goal: check whether an application behaves as expected
Safeguard against unwanted behavior when changes are made
Automated, and thus efficient in the long-term
What to Test
Have a test priority (example):
Demo App Setup
Download zip file or clone this repo
Unzip, if you downloaded the zip file.
cd into project folder
run
yarn install && yarn dev
login in with username: 'johndoe' and password: 's3cret'
Unit Tests
Test a very small part of the code, usually a function.
create a new test file for whichever component you want to test. (i.e., component name is TransactionCreateStepTwo.js then test file name would be TransactionCreateStepTwo.test.js) (example test file: transactionCreateStepTwo.test.js
render the component you want to test
screen.debug()
- debug() - shortcut for console.log(prettyDOM(baseElement)) - if the baseElement is not specified, it defaults todocument.body
screen.getByRole("button", { name: /pay/i })).toBeDisabled();
- getByRole - react-testing-library query to see what roles are availablenow create your assertion statement
expect(await screen.findByRole("button", { name: /pay/i })).toBeEnabled();
which should fail nownow create your assertion statement
expect(await screen.findByRole("button", { name: /pay/i })).toBeDisabled();
which should pass nowuserEvent.type(screen.getByPlaceholderText(/amount/i), "50");
- mimics a user typing an amount in the textbox for amount - on this example have to usegetByPlaceholderText
because on this form there is no label or name associated with it.userEvent.type(screen.getByPlaceholderText(/add a note/i), "dinner");
- mimics a user typing a note in the textbox for note - on this example have to usegetByPlaceholderText
because on this form there is no label or name associated with it.
Here we tested user interaction and conditional rendering by first seeing if the 'pay' button was disabled on initial render, added user interaction by typing in values to the two inputs and then testing to make sure the 'pay' button became enabled.
Integration Tests
Testing whether multiple units within the app are working correctly together. These types of tests usually have multiple assertions in them. There is not a consensus on what makes a unit test vs. an integration test.
When writing an integration test, it is good to think about actual user flows. Example: user clicks on '$ NEW', searches for another user, clicks on the appropriate user and then fills in amount, adds a note and then clicks on 'PAY' button.
A good example of a simple integration test then would be to combine the two unit tests written in the last section by moving the assertion from the first test into the second test before the user interaction and then deleting the first test.
This does not mean this is how integration tests are to be written, but if combining more than one unit test represents how a realistic user flows, then by all means do this. In this example, it just worked out that way.
Often, a few integration tests are better than numerous, small and concise unit tests.
End-to-End Tests
Tests all the way from the back end to the front end. In essence, mimicking how a user would interact and use the application in the browser.
For this, a testing library known as Cypress will be used. This does not come with CreateReactApp by default, so there is some setup involved.
install Cypress
open cypress
remove Cypress default tests by navigating to '/cypress/integration' and remove any folders in there.
(optional) add react-testing-library cypress commands—this allows you to use the commands from the previous tests in out cypress tests
go to '/cypress/support/commands.js'
add the following
import '@testing-library/cypress/';close all open files
(optional) if not already installed, then add Testing Playground Chrome extension - allows you to hover over elements of your page and suggests queries [//]: # (TODO: fix link to payments.spec.js)
go to '/cypress/integration' folder and add [[javascript.react-js.testing.react-testing-crash-course.examples.payment_specjs]]
in the cypress window, click on 'Run 1 integration spec' to see the results of the test
line 37 in paymentSpec Example cy.findByText(note).click({ force: true });
forces cypress to click regardless. If this is the .click({ force: true })
is not included, cypress may error out since it takes the page some time to fully load and the element it is supposed to click maybe hidden from view by the navbar.
lines 40 & 41 in paymentSpec Example
are cypress version of assertion statements
line 47 in paymentSpec Example expect(convertedOldBalance - convertedNewBalance).to.equal(parseFloat(paymentAmount));
is a custom assertion