Testing Lab 5: Container Components
Objectives
- Test Setup
- Test the Loading Indicator Displays
- Test the Projects Display
- Test the More Button Displays
- Test the Error Displays
Steps
Test Setup
Create the file
src\projects\__tests__\ProjectsPage-test.js
.Add the setup code below to test the component.
src\projects\__tests__\ProjectsPage-test.js
import React from "react";
import { MemoryRouter } from "react-router-dom";
import { Provider } from "react-redux";
import { store } from "../../state";
import ProjectsPage from "../ProjectsPage";
import {
render,
screen,
waitForElementToBeRemoved,
} from "@testing-library/react";
describe("<ProjectsPage />", () => {
function renderComponent() {
render(
<Provider store={store}>
<MemoryRouter>
<ProjectsPage />
</MemoryRouter>
</Provider>
);
}
test("should render without crashing", () => {
renderComponent();
expect(screen).toBeDefined();
});
});Verify the initial test passed.
PASS src/projects/__tests__/ProjectsPage-test.js
! Check to make sure the
delay
function used to delay the backend query and display the loading indicator has been removed inprojectAPI.js
. The delay call causes themsw
library to throw an error.
Test the Loading Indicator Displays
Test that the loading indicator displays when the component initially renders.
src\projects\__tests__\ProjectsPage-test.js
import React from 'react';
import { MemoryRouter } from 'react-router-dom';
import { Provider } from 'react-redux';
import { store } from '../../state';
import ProjectsPage from '../ProjectsPage';
import {
render,
screen,
waitForElementToBeRemoved,
} from '@testing-library/react';
describe('<ProjectsPage />', () => {
function renderComponent() {
render(
<Provider store={store}>
<MemoryRouter>
<ProjectsPage />
</MemoryRouter>
</Provider>
);
}
...
+ test('should display loading', () => {
+ renderComponent();
+ expect(screen.getByText(/loading/i)).toBeInTheDocument();
+ });
});Verify the test passed.
PASS src/projects/__tests__/ProjectsPage-test.js
Test the Projects Display
Open a
command prompt
(Windows) orterminal
(Mac).Change the current directory to
code\keeptrack
.Run one of the following sets of commands to install
Mock Service Worker
to mock the HTTP requests.$ npm install msw@1 --save-dev
# or
$ yarn add msw@1 --devExport the url used in the component from the
projectAPI.js
file.src\projects\projectAPI.js
import { Project } from './Project';
const baseUrl = 'http://localhost:4000';
- const url = `${baseUrl}/projects`;
+ export const url = `${baseUrl}/projects`;
...Add the setup code to mock the requests.
src\projects\__tests__\ProjectsPage-test.js
import React from 'react';
import { MemoryRouter } from 'react-router-dom';
import { MOCK_PROJECTS } from '../MockProjects';
import { Provider } from 'react-redux';
import { store } from '../../state';
import ProjectsPage from '../ProjectsPage';
import {
render,
screen,
waitForElementToBeRemoved,
} from '@testing-library/react';
+ import { rest } from 'msw';
+ import { setupServer } from 'msw/node';
+ import { url as projectsUrl } from '../projectAPI';
+ import { MOCK_PROJECTS } from '../MockProjects';
+ // declare which API requests to mock
+ const server = setupServer(
+ // capture "GET http://localhost:3000/projects" requests
+ rest.get(projectsUrl, (req, res, ctx) => {
+ // respond using a mocked JSON body
+ return res(ctx.json(MOCK_PROJECTS));
+ })
+ );
describe('<ProjectsPage />', () => {
function renderComponent() {
render(
<Provider store={store}>
<MemoryRouter>
<ProjectsPage />
</MemoryRouter>
</Provider>
);
}
+ beforeAll(() => server.listen());
+ afterEach(() => server.resetHandlers());
+ afterAll(() => server.close());
test('should render without crashing', () => {
renderComponent();
expect(screen).toBeDefined();
});
test('should display loading', () => {
renderComponent();
expect(screen.getByText(/loading/i)).toBeInTheDocument();
});
});Test that the projects display after the mocked data is returned.
src\projects\__tests__\ProjectsPage-test.js
...
describe('<ProjectsPage />', () => {
function renderComponent() {
render(
<Provider store={store}>
<MemoryRouter>
<ProjectsPage />
</MemoryRouter>
</Provider>
);
}
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
...
+ test('should display projects', async () => {
+ renderComponent();
+ expect(await screen.findAllByRole('img')).toHaveLength(
+ MOCK_PROJECTS.length
+ );
+ });
});Verify the test passed.
PASS src/projects/__tests__/ProjectsPage-test.js
Test the More Button Displays
Test that the More button displays after the projects have loaded.
src\projects\__tests__\ProjectsPage-test.js
...
import {
render,
screen,
waitForElementToBeRemoved,
} from '@testing-library/react';
...
describe('<ProjectsPage />', () => {
...
+ test('should display more button', async () => {
+ renderComponent();
+ expect(
+ await screen.findByRole('button', { name: /more/i })
+ ).toBeInTheDocument();
+ });
+
+ // this tests the same as the last test but demonstrates
+ // what find* methods are doing
+ test('should display more button with get', async () => {
+ renderComponent();
+ await waitForElementToBeRemoved(() => screen.queryByText(/loading/i));
+ expect(screen.getByRole('button', { name: /more/i })).toBeInTheDocument();
+ });
});Verify the test passed.
PASS src/projects/__tests__/ProjectsPage-test.js
Test the Error Displays
Test that a custom error displays when a server error is returned.
src\projects\__tests__\ProjectsPage-test.js
...
import {
render,
screen,
waitForElementToBeRemoved,
} from '@testing-library/react';
...
describe('<ProjectsPage />', () => {
...
+ test('should display custom error on server error', async () => {
+ server.use(
+ rest.get(projectsUrl, (req, res, ctx) => {
+ return res(ctx.status(500, 'Server error'));
+ })
+ );
+ renderComponent();
+
+ expect(
+ await screen.findByText(/There was an error retrieving the project(s)./i)
+ ).toBeInTheDocument();
+ });
});Verify the test passed.
PASS src/projects/__tests__/ProjectsPage-test.js