Let’s walk through a typical Jest test suite built for a Gatsby site. This will demonstrate a standard configuration of Jest that integrates seamlessly with Gatsby’s build process. To begin, we’ll spin up a new version of the Gatsby blog starter:
$
gatsby
new
gtdg-ch11-unit-testing
gatsbyjs/gatsby-starter-blog
$
cd
gtdg-ch11-unit-testing
Jest requires us to install several dependencies. We need to install babel-jest
and babel-preset-gatsby
in order to ensure that the Babel presets Jest will use match those that are found in our Gatsby site, since Gatsby uses its own unique Babel configuration (this is particularly important if you have an unusual Babel preset that was customized beyond Gatsby’s own preset and needs to be accounted for in your unit testing):
$
npm
install
--save-dev
jest
babel-jest
react-test-renderer
\
babel-preset-gatsby
identity-obj-proxy
In the same directory as our gatsby-config.js file (i.e., in the root of the Gatsby project), we’ll add a jest.config.js file that contains some defaults and also instructs Jest to use the babel-jest
package:
// 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: ``,
setupFiles: [`<rootDir>/loadershim.js`],
}
The transform
object instructs Jest to transform all files with the .js or .jsx extension using a jest-preprocess.js file, which Jest expects to encounter in the project root. Let’s go ahead and add this file now to tell Jest that it should use Gatsby’s Babel preset when setting up this transformer:
// jest-preprocess.js
const
babelOptions
=
{
presets
:
[
"babel-preset-gatsby"
],
}
module
.
exports
=
require
(
"babel-jest"
)
.
default
.
createTransformer
(
babelOptions
)
The moduleNameMapper
object indicates to Jest how it should handle imports, much like Webpack rules. Because Jest is incapable of handling static file imports as mocks (dummy modules that don’t do anything), we need to manually create mocks that represent assets. For stylesheets such as layout.css, we’ll use the identity-obj-proxy
package to handle this for us. For all other assets, however, we’ll need to create a directory in the project root named __mocks__ and add a manual mock named file-mock.js:
// __mocks__/file-mock.js
module
.
exports
=
"test-file-stub"
The testPathIgnorePatterns
array tells Jest which directories to ignore—in this case, the node_modules, .cache, and public directories, which aren’t things we need to unit-test.
The transformIgnorePatterns
array is important, and may differ from other Jest implementations you are familiar with. Because the Gatsby framework by default includes ES6 code that remains untranspiled as a rule, we need to indicate to Jest that files containing code not meant to be transpiled shouldn’t be transformed by instructing Jest to ignore the Gatsby module. Otherwise, we’ll encounter an error that looks like this:
/my-app/node_modules/gatsby/cache-dir/gatsby-browser-entry.js:1
({
"Object.<anonymous>"
:function(
module,exports,require, >>>
__dirname,__filename,global,jest){
import React from "react"
^^^^^^
SyntaxError: Unexpected token import
The globals
object defines the __PATH_PREFIX__
string, which Gatsby sets itself and might be required by some components.
The testURL
string identifies a valid URL rather than the default of about:blank
, which doesn’t work with localStorage
(an issue resolved in Jest 23.5.0 and later).
The setupFiles
array permits us to enumerate files that should be included before tests are run. setupFiles
cannot accept a function, so we’ll need to create another file, loadershim.js, in the project root containing the following code:
// loadershim.js
global
.
___loader
=
{
enqueue
:
jest
.
fn
()
}
TIP
If you need to modify your Babel configuration, perform those modifications in the jest-preprocess.js file. See Section 14 and consult the Gatsby Babel configuration guide for more information.
Before we proceed to writing and running unit tests in Jest for Gatsby, one final step that may be useful is to create a mock for the Gatsby module itself in the __mocks__ directory under the filename gatsby.js. If you’re using native Gatsby exports like Link
or graphql
, this will help you avoid errors down the road by ensuring they’re not absent:
// __mocks__/gatsby.js
const
React
=
require
(
"react"
)
const
gatsby
=
jest
.
requireActual
(
"gatsby"
)
module
.
exports
=
{
...
gatsby
,
graphql
:
jest
.
fn
(),
Link
:
jest
.
fn
().
mockImplementation
(
// These props cannot be used on `a` elements
({
activeClassName
,
activeStyle
,
getProps
,
innerRef
,
partiallyActive
,
ref
,
replace
,
to
,
...
rest
})
=>
React
.
createElement
(
"a"
,
{
...
rest
,
href
:
to
,
})
),
StaticQuery
:
jest
.
fn
(),
useStaticQuery
:
jest
.
fn
(),
}
Mocking ensures Jest tests won’t hang when they come across the graphql
function, Link
component, and StaticQuery
component in your Gatsby code.
NOTE
If you’re using TypeScript to write your Gatsby site, your Jest configuration will require additional dependencies to handle typings, different transformer configuration approaches, and, if you’re using tsconfig
paths, other settings that are accounted for in the Jest with TypeScript section of the Gatsby documentation.
Leave a Reply