Setting Up Gatsby for Unit Testing
We are going to be using the Jest testing framework for react which was developed by Facebook. So first, we install all the Jest dependencies that play well with Gatsby.
I’m not going to go into detail of how all these configuration files function since the Gatsby Unit Testing Config documentation already walks through all the files in detail.
Install Dependencies
# power down the app and cd into frontend on your local computer
npm install --save-dev jest@26.6.3 babel-jest@26.6.3 react-test-renderer babel-preset-gatsby identity-obj-proxy
At the time of this writing babel-jest version 27.0.0 breaks with gatsby. As a work-around we installed babel-jest version 26.6.3 along with jest version 26.6.3.
We need to create five configuration files for Jest:
- jest.config.js
- jest-preprocess.js
- __mocks__/file-mock.js
- loadershim.js
- __mocks__/gatsby.js
Create Config File for Jest
// frontend/jest.config.js
module.exports = {
transform: {
"^.+\\.jsx?$": `<rootDir>/jest-preprocess.js`,
},
moduleNameMapper: {
".+\\.(css|styl|less|sass|scss)$": `identity-obj-proxy`,
".+\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": `<rootDir>/__mocks__/file-mock.js`,
},
testPathIgnorePatterns: [`node_modules`, `\\.cache`, `<rootDir>.*/public`],
transformIgnorePatterns: [`node_modules/(?!(gatsby)/)`],
globals: {
__PATH_PREFIX__: ``,
},
testURL: `http://localhost`,
setupFiles: [`<rootDir>/loadershim.js`],
}
Create jest-preprocess.js
const babelOptions = {
presets: ["babel-preset-gatsby"],
}
module.exports = require("babel-jest").createTransformer(babelOptions)
Create __mocks__/file-mock.js (note double underscores)
// frontend/__mocks__/file-mock.js
module.exports = "test-file-stub"
Create Global loadershim.js
// frontend/loadershim.js
global.___loader = {
enqueue: jest.fn(),
}
Mock Gatsby
# frontend/__mocks__/gatsby.js
const React = require("react")
const gatsby = jest.requireActual("gatsby")
module.exports = {
...gatsby,
graphql: jest.fn(),
Link: jest.fn().mockImplementation(
// these props are invalid for an `a` tag
({
activeClassName,
activeStyle,
getProps,
innerRef,
partiallyActive,
ref,
replace,
to,
...rest
}) =>
React.createElement("a", {
...rest,
href: to,
})
),
StaticQuery: jest.fn(),
useStaticQuery: jest.fn(),
}
Finally we need to update our package.json file where the default test script has not been set to jest.
// frontend/package.json
"scripts": {
"build": "gatsby build",
"develop": "gatsby develop",
"format": "prettier --write \"**/*.{js,jsx,ts,tsx,json,md}\"",
"start": "npm run develop",
"serve": "gatsby serve",
"clean": "gatsby clean",
"test": "jest"
Creating a Header Component
Before we begin writing tests lets build a header component to display the title of our site. The component below is based is pulled from the Gatsby.js documentation for testing components with GraphQL where they have a detailed breakdown of all the elements in this header that will enable us to test with Jest.
Start by creating a new directory and header.js file:
mkdir frontend/src/components
touch frontend/src/components/header.js
… and populate the header.js file with the following:
// frontend/src/components/header.js
// 1)
import { Link } from "gatsby"
// 2)
import PropTypes from "prop-types"
// 3)
import React from "react"
// 4)
import { useStaticQuery, graphql } from "gatsby"
// 5)
export const PureHeader = ({ data }) => (
// 6)
<header
style={{
background: `rebeccapurple`,
marginBottom: `1.45rem`,
}}
>
<div
style={{
margin: `0 auto`,
maxWidth: 960,
padding: `1.45rem 1.0875rem`,
}}
>
<h1 style={{ margin: 0 }}>
// 7)
<Link
to="/"
style={{
color: `white`,
textDecoration: `none`,
}}
>
// 8)
{data.site.siteMetadata.title}
</Link>
</h1>
</div>
</header>
)
// 9)
export const Header = props => {
// 10)
const data = useStaticQuery(graphql`
query {
site {
siteMetadata {
title
}
}
}
`)
// 11)
return <PureHeader {...props} data={data}></PureHeader>
}
// 12)
Header.propTypes = {
siteTitle: PropTypes.string,
}
// 13)
Header.defaultProps = {
siteTitle: ``,
}
// 14
export default Header
Structure of a Gatsby Component
Lets break down what we are building in this header component step by step.
// 1)
import { Link } from "gatsby"
We start by importing the Gatsby Link component which allows us to link between internal pages in Gatsby. When we want to navigate between internal Gatsby pages we will by using the Link tag instead of traditional anchor (a) tags.
// 2)
import PropTypes from "prop-types"
We will be using propTypes which help us to keep track if our component is receiving the correct type data or not.
// 3)
import React from "react"
Of course we import react since Gatsby is built on top of React.
// 4)
import { useStaticQuery, graphql } from "gatsby"
We will be using useStaticQuery, (a React hook), and graphql to query the siteMetadata for the title we’ve set for our site in gatsby-config.js.
// 5)
export const PureHeader = ({ data }) => (
Its this function PureHeader we will be testing with Jest. More on this later.
// 6)
<header
style={{
background: `rebeccapurple`,
marginBottom: `1.45rem`,
}}
>
We define our HTML header element with some inline styling.
// 7)
<Link
to="/"
style={{
color: `white`,
textDecoration: `none`,
}}
>
Using the Gatsby Link component we link the header to the index page of the site.
// 8)
{data.site.siteMetadata.title}
Using the data prop we passed into our function, we pull the title of our site from Gatsby-config.js. Note the curly braces. Anytime we want to embed a javascript snippet or in this case our {data.site.siteMetadata.title} variable, we wrap the javascript code in braces.
// 9)
export const Header = props => {
This is the second function where we will use useStaticQuery and graphql to query the siteMetadata in the Gatsby-config.js file.
// 10)
const data = useStaticQuery(graphql`
query {
site {
siteMetadata {
title
}
}
}
`)
Here we define the query with graphql and execute the query with useStaticQuery.
// 11)
return <PureHeader {...props} data={data}></PureHeader>
We return the PureHeader with the data prop containing the siteMetadata.
// 12)
Header.propTypes = {
siteTitle: PropTypes.string,
}
Now we can implement propTypes for our component which in this case is siteTitle of the PropType string. This isn’t absolutely necessary for the Gatsby component to render properly but it is a good coding practice to explicitly enforce type casting in our app.
// 13)
Header.defaultProps = {
siteTitle: ``,
}
Here we define the defaultProps which in this case is an empty string but, if we were to import this component into a page and not pass in the siteTitle argument we could define a default siteTitle.
// 14)
export default Header
Finally we export our Header function to make available for import into a page or another component.
What is a Pure Function and Why Use It?
We have two functions in this header.js file:
- export const PureHeader = ({ data }) => ( // … code
- export const Header = props => { // … code
Of the two the first is a pure function. What is a pure function?
A pure function is a deterministic function. This means when a same input is passed every time, the function will return same output.
A pure function will have the following properties:
- It depends only on its own arguments
- It wont try to change variables out of its scope
- It doesn’t produce any side effects
Its the first bullet point that concerns us. Our PureHeader only deals with the argument, ({data}), that was passed by the Header function.
The second Header function is not pure because its changing variables out of its scope when it runs useStaticQuery and graphql to query siteMetadata.
Why does this matter?
If we were to write a jest test and run it against the second Header function the test would fail because Jest needs to mock the query itself before running the test. By testing the PureHeader we can use Jest to mock the query and pass in our mocked data into the imported PureHeader.
What an elegant solution to the problem of testing GraphQL components!
Before we write our Jest header.js test lets import the Header into our index.js file.
Import Header Component into Index File
Lets refactor our index page to import our Header function by adding:
// frontend/src/pages/index.js
// code ...
import Header from "../components/header"
// code ...
<Header />
The complete updated index.js file is follows:
// frontend/src/pages/index.js
import React from "react"
import { useQuery } from '@apollo/client';
import gql from 'graphql-tag';
import Header from "../components/header"
const APOLLO_QUERY = gql`
query {
todo(id: 1) {
id
title
task
}
}
`;
export default function Home() {
const { loading, error, data } = useQuery(APOLLO_QUERY)
if (loading) return <p>Loading...</p>;
if (error) return <p>Error!</p>;
return(
<div>
<Header />
<div>
Hello world!
</div>
<div>
Todo Title: {data.todo.title}
</div>
</div>
)
}
Configure Site Meta Data for Gatsby
We now can define global meta data properties in gatsby-config .js file where we will import our site’s into our index file.
// frontend/gatsby-config.js
/**
* Configure your Gatsby site with this file.
*
* See: https://www.gatsbyjs.com/docs/gatsby-config/
*/
module.exports = {
siteMetadata: {
title: `Gatsby Todo App`,
description: `A fabulous description of our Todo app here...`,
author: `Ron Leeson`,
},
plugins: [],
}
Writing Our First Test
The first test we write will be for the Header component by creating a directory in our components directory for __tests__ and a file for the header.js test.
mkdir frontend/src/components/__tests__
touch frontend/src/components/__tests__/header.js
Populate the header.js file with the following:
// frontend/src/components/__tests__/header.js
import React from 'react'
// 1)
/**
Render the React component to a pure JavaScript objec without depending on the DOM
**/
import renderer from 'react-test-renderer'
// 2) Import our PureHeader we just built
import { PureHeader as Header } from '../header'
// 3)
/**
Using the Jest describe method for containing our test
**/
describe("Header", () => {
it("renders correctly", () => {
// Created using query from Header.js
const data = {
site: {
siteMetadata: {
title: "Gatsby Todo App"
},
},
}
// 4) Make a snapshot of the PureHeader
const tree = renderer.create(<Header data={data} />).toJSON()
// 5)
/**
On the initial test we take the snapshot, on subsequent tests
we compare the new snapshot against the initial snapshot for
a match. If they match test passes, if they don't match
the test fails.
**/
expect(tree).toMatchSnapshot()
})
})
With all dependencies installed, our new Header component imported into our refactored index file, we are now ready to rebuild the frontend docker container.
Rebuild Frontend Docker Container
docker-compose build frontend
If you run into some errors while building or booting up the app you can tell Docker to clear the cache before building the frontend container:
docker-compose build --no-cache frontend
Now we are ready to boot up and see if our index page renders:
docker-compose -f docker-compose.yml -f docker-compose-dev.yml up
# Or using the Makefile command
make dev
Going to http://localhost:8000 in your browser you should see something like this:

If you get an error because the Django backend server failed to boot, try starting the backend as follows:
docker-compose up -d database
docker-compose up -d server
# now start the frontend in development mode
make dev
Running Jest Tests
To run tests in Jest here are a few of the available commands:
- npm test // Run all the tests once
- npm test — –watch // Tell Jest to watch for changes in code and run tests again
- npm test — –u // Update snapshots
Since we are running Jest in our docker frontend container the command to run a test looks like:
# this will take the intial snapshot of the Header component
docker-compose run frontend npm test
# run the test again and changes to the Header will be compared against the inital snapshot
docker-compose run frontend npm test
Running the commands above you should see like the following in your terminal:

Jest Testing Apollo/Client Queries
Its considered best practice to test the smallest component possible rather than test a full page. So, instead of testing the full index.js page, we can move the GET_TODO_QUERY out of index.js into a separate todo.js component.
Start by creating a file for todo.js:
// frontend/src/components/todo.js
import React from "react"
import { gql, useQuery } from '@apollo/client'
import PropTypes from "prop-types"
export const GET_TODO_QUERY = gql`
query GetTodo($id: Int) {
todo(id: $id) {
id
title
task
}
}
`;
export const Todo = ({ id }) => {
const { loading, error, data } = useQuery(
GET_TODO_QUERY,
{ variables: { id }}
);
if (loading) return <p>Loading...</p>;
if (error) return <p>Error!</p>;
return (
<div>
<h5>Todo:</h5>
id: {data.todo.id} <br />
title: {data.todo.title}
</div>
)
}
Todo.propTypes = {
id: PropTypes.number.isRequired
}
Todo.defaultProps = {
id: ``,
}
export default Todo
Refactor the index.js file to import our new Todo component:
// frontend/src/pages/index.js
import React from "react"
import Header from "../components/header"
import Todo from "../components/todo"
export default function Home() {
return(
<div>
<Header />
<div>
Hello world!
</div>
<Todo id = {2} />
</div>
)
}
Testing Todo Component with GraphQL
import React from "react"
import { MockedProvider } from '@apollo/client/testing';
import renderer from "react-test-renderer"
// The component AND the query need to be exported
import { GET_TODO_QUERY, Todo } from '../todo';
const mocks = [
{
request: {
query: GET_TODO_QUERY,
variables: {
id: 1,
},
},
result: {
data: {
todo: { id: '1', title: 'Test' },
},
},
},
];
it('renders without error', () => {
renderer.create(
<MockedProvider mocks={mocks} addTypename={false}>
<Todo id={1} />
</MockedProvider>,
);
});
Run the Jest Test Suite
docker-compose run frontend npm test
# or if you want Jest to run in the background watching for changes
docker-compose run frontend npm test -- --watchAll
# if you want to update your snapshots
docker-compose run frontend npm test -- --u

And there we have it, testing for Gatsby page queries and Apollo graphql queries.
Having to type out these long docker-compose commands to run tests can get old so, lets update our Makefile with some shortcuts for our Gatsby tests and while we are at it, lets add shortcuts for our Django backend tests as well. Remember those? I know, it was a long time ago…
build:
docker-compose build
dev:
docker-compose -f docker-compose.yml -f docker-compose-dev.yml up
dev-down:
docker-compose -f docker-compose.yml -f docker-compose-dev.yml down
dev-build-no--cache:
docker-compose -f docker-compose.yml -f docker-compose-dev.yml build --no-cache frontend
prod:
docker-compose up -d
prod-down:
docker-compose down
# Django Backend Testing
dj-test:
docker-compose run server ./manage.py test
pytest:
docker-compose run server pytest
pytest-verbose:
docker-compose run server pytest -vv
# Gatsby Frontend Testing
test:
docker-compose run frontend npm test
watch:
docker-compose run frontend npm test -- --watchAll
update-snapshot:
docker-compose run frontend npm test -- --u
up-non-daemon:
docker-compose up
start:
docker-compose start
stop:
docker-compose stop
down:
docker-compose down
restart:
docker-compose stop && docker-compose start
restart-dev:
docker-compose down && docker-compose -f docker-compose.yml -f docker-compose-dev.yml up -d
restart-frontend:
docker-compose stop frontend && docker-compose start frontend
restart-server:
docker-compose stop server && docker-compose start server
shell-server:
docker exec -ti server bash
shell-frontend:
docker exec -ti frontend bash
shell-db:
docker exec -ti postgres bash
log-server:
docker-compose logs server
log-frontend:
docker-compose logs frontend
log-db:
docker-compose logs postgres
collectstatic:
docker exec server /bin/sh -c "python manage.py collectstatic --noinput"
migrations:
docker exec server /bin/sh -c "python manage.py makemigrations; python manage.py migrate"
Lets run all our tests to date for both front and back ends:
# Run Gatsby tests
make test
# Run Django pytests
make pytest
And I am pleased as punch to see all my tests pass. Woohoo! We haven’t broken anything. If we had some fails we would know where to go to fix them. Got to love automated testing. 🙂
Conclusion
Now that we have…
- Our Gatsby/Docker development environment setup
- Gatsby configured to use the Apollo/Client
- Gatsby configured for using Jest to test both page and Apollo queries
… we have all of the basic infrastructure we need to build out our Gatsby Todo app frontend.
Next up in chapter 11 we will restructure our Gatsby project to implement a layout component which allows us to share markup, styles, and functionality across multiple pages.