Frontend testing
“BUG” is a huge emotional word for
Software Engineers
by Israel Romero and Thomas Truong
TOPICS
- React Testing Library
- Test Examples
- Setup
- Extension
- Build
- Coverage
- Future
react Testing library
A library providing the implementation (methods/functions) to mount React components (in a virtual DOM) + the means to access (and interact) with the tree that is created as a result.
- Mounts components = Integration testing
- It is just a library , we need a test runner (Jest)
- It accounts for the asynchronous nature of Web components
- Almost always we will need mocking, but that is not RTL itself
get By
import { useEffect, useState } from "react";
import "./App.css";
function App() {
const [name, setName] = useState();
useEffect(() => {
fetch("https://swapi.dev/api/people/1")
.then((response) => response.json())
.then((data) => {
setName(data.name);
});
}, []);
return (
<>
<h1>Star Wars</h1>
<span>name: {name}</span>
</>
);
}
export default App;
import { render, screen } from "@testing-library/react";
import App from "./App";
it("renders text", () => {
render(<App />);
expect(screen.getByText(/star wars/i)).toBeVisible();
});
find BY
import { useEffect, useState } from "react";
import "./App.css";
function App() {
const [name, setName] = useState();
useEffect(() => {
fetch("https://swapi.dev/api/people/1")
.then((response) => response.json())
.then((data) => {
setName(data.name);
});
}, []);
return (
<>
<h1>Star Wars</h1>
<span>name: {name}</span>
</>
);
}
export default App;
import { render, screen } from "@testing-library/react";
import App from "./App";
it("renders Luke", async () => {
render(<App />);
expect(await screen.findByText(/Luke/i)).toBeVisible();
});
QUERY BY
import { useEffect, useState } from "react";
import "./App.css";
function App() {
const [name, setName] = useState();
useEffect(() => {
fetch("https://swapi.dev/api/people/1")
.then((response) => response.json())
.then((data) => {
setName(data.name);
});
}, []);
return (
<>
<h1>Star Wars</h1>
<span>name: {name}</span>
</>
);
}
export default App;
import { render, screen } from "@testing-library/react";
import App from "./App";
it("something that is not there", () => {
render(<App />);
expect(screen.queryByText(/Darth Vader/i)).toBeNull();
});
Mocking
- Mocking modules is bread and butter of testing
- Provided by your test runner (Jest)
import { render, screen } from "@testing-library/react";
import "@testing-library/jest-dom";
import App from "./App";
const mockFetch = jest
.fn()
.mockReturnValue(
Promise.resolve({ json: () => Promise.resolve({ name: "Luke Skywalker" }) })
);
window.fetch = mockFetch;
describe("App Component", () => {
beforeEach(() => {
render(<App />);
});
it("Title is displayed", () => {
expect(screen.getByText(/star wars/i)).toBeVisible();
});
it("Luke is displayed after the fetch", async () => {
expect(screen.queryByText(/luke/i)).toBeNull();
expect(mockFetch).toHaveBeenCalled();
expect(await screen.findByText(/luke/i)).toBeVisible();
});
});
Test EXAMPLES
We will be presenting a test demo in Web Members. It can be accessed at the link below:
setup
react-scripts
React-scripts
is a preconfigured set of commands that come out of the box with create-react-app
.
react-scripts test
command expects the tests folder to follow a certain conversation and doesn't work well with extensions.
JEST
- Add a new file json.config.json
- Add ts-jest to package.json
- Add '@babel/preset-env' to .babelrc
scripts: {
...
"test": "jest",
...
}
Config
{
"preset": "ts-jest",
"testEnvironment": "jsdom",
"transform": {
"^.+\\.(js|jsx)$": "babel-jest"
},
"moduleNameMapper": {
"\\.(svg|woff|png|mp3)": "<rootDir>/src/__mocks__/fileMock.ts"
},
"setupFiles": ["<rootDir>/src/setupTests.js"],
"testPathIgnorePatterns": [
"/node_modules/",
"src/components/calendars/**/AddAvailabilityForm.test.tsx",
]
}
Babel
{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
],
}
jEST vscode EXTENSION
https://marketplace.visualstudio.com/items?itemName=Orta.vscode-jest
tEST EXPLORER
Running test
Demo
debugging test
Demo
build
SCRIPTs
"test": "jest",
"test:ci": "yarn test --maxWorkers=2 --watchAll=false",
"test:ci:cov": "yarn test:ci --coverage",
"test:cov": "yarn test --coverage",
pr template
## Description
_Please include a summary of the changes_
**Link to JIRA ticket**
## Checklist
- [ ] Tested with feature branch
- [ ] Added unit test for feature
feature branch
- name: Run test
run: yarn test:ci:cov --json --outputFile=jest-results.json
- name: Post test results 🚨
if: failure()
uses: im-open/process-jest-test-results@v2.1.3
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
results-file: 'jest-results.json'
deploy to dev, prod
jobs:
development:
steps:
- name: Run test
run: yarn test:ci:cov
- name: Build
run: yarn build
production:
steps:
- name: Run test
run: yarn test:ci
coverage
badge
- name: Run test
run: yarn test:ci:cov
- name: Add coverage badge
uses: we-cli/coverage-badge-action@main
threshold
"coverageThreshold": {
"./src/components/calendars/FullCalendar/components/appointments/": {
"lines": 43
},
"./src/helpers/": {
"lines": 20
}
}
jest.config.json
report
./coverage/lcov-report/index.html
FUTURE
coverage on github pages
PULL REQUEST COVERAGE
CODE QUALITY/COVERAGE TOOL
tdd/bdd
conclusion
PROS
CONS
- Refactor code with confidence
- Great way to document code
- Find unexpected bugs
- Unit test can't catch every error (false confidence)
- Learning curve for writing unit test
- Slower build
TESTING... COMPLETE
Frontend Testing
By Thomas Truong
Frontend Testing
- 43