Mocking
Mocking services and child components
Child components
import Child from "./child";
const ComponentWithChild = ({foo}) => {
return (<div>
<Child foo={foo}>Data sent to child</Child>
</div>)
}
export default ComponentWithChild
Since Jest will render the entire tree, the <Child/> component will also get rendered. In some cases, we don't want to render the structure of <Child/> and just the props or contents we pass into it. We can easily mock the <Child/> component and make it render whatever we pass into it for testing purposes.
import React from "react";
import {render, screen} from "test-utils";
import userEvent from "@testing-library/user-event";
import "@testing-library/jest-dom";
import ComponentWithChild from './component-with-child'
jest.mock("./child", ({foo, children}) => (
<div>
<div aria-label="foo">
{foo}
</div>
<p>
{children}
</p>
</div>
))
describe("component-with-child", () => {
test("should render foo and children", () => {
const bar = "bar"
render(<ComponentWithChild foo={bar} />);
expect(screen.getByRole('div', {name: "foo"})).toHaveTextContent(bar);
expect(screen.getByRole('p')).toHaveTextContent("Data sent to child");
})
})
<div>
<div>
<div aria-label="foo">
bar
</div>
<p>
Data sent to child
</p>
</div>
</div>
jest.mock takes in a string to intercept any imports matching that string. We are telling jest to pass in this new JSX component to anything using "./child" as an import.
Learn more about mocking best practices here
Services
How to mock and expect results from API services
import React from "react";
import {render, screen} from "test-utils";
import userEvent from "@testing-library/user-event";
import "@testing-library/jest-dom";
import ComponentThatUsesApi from "./component";
jest.mock("@app/services/api/someApi");
const api = require("@app/services/api/someApi");
describe("component-that-uses-api", () => {
test("should render result passed into getResult", () => {
const result = "Hi Klipfolio!";
api.getResult.mockReturnValue(result);
render(<ComponentThatUsesApi />);
expect(api.getResult).toHaveLastBeenCalledWith(result);
expect(screen.getByRole('div', {name: "result"})).toHaveTextContent(result);
})
})
We are telling jest to mock the entire file of the service
someApi, now this could suffice but you wouldn't be able to expect and change results from the service functionsSo we require (not import) since we want to get the API service as the mocked version which has to happen after
jest.mockThis returns mock functions of all the exported functions in the service API.
Read up on mock functions API here
Other ways to mock
Jest has a mockImplementation function that allows mocking a single value.
import React from "react";
import {render, screen} from "test-utils";
import userEvent from "@testing-library/user-event";
import "@testing-library/jest-dom";
import ComponentThatImportsFunction from "./component";
import Function from './function'
describe("ComponentThatImportsFunction", () => {
test("mocking of Function", () => {
const mock = Function.mockImplementation((someArg) => `klipfolio.com/${someArg}`);
render(<ComponentThatImportsFunction />);
expect(mock).toHaveBeenCalledOnce(result);
})
})
Automatic component mocks
Mocking your component for everyone else
├── jest.config.js
├── src
│ ├── main
│ │ └── js
│ │ └── components
│ │ └── AboutMetrics
│ │ ├── __mocks__
│ │ │ └── AboutMetrics.js
│ │ └── AboutMetrics.js
│ └── test
│ └── react-testing-library
│ ├── mocks
│ │ └── ...
│ ├── test
│ │ └── ...
│ └── utils
│ └── ...
Jest will automatically pick up files in __mocks__ directory beside any components. If a file here has the same name as the component you are trying to mock, it will be automatically mocked for anyone using this component.
If you wish to not mock the component that is automatically mocked because of this you can use jest.unmock("@app/components/AboutMetics") in this case.