Setting up environment
npm install --save-dev jest babel-jest react-test-renderer babel-preset-gatsby identity-obj-proxy
babel-jest babel-preset-gatsby
- the last two ensure that the babel preset(s) that are used match what are used internally for your Gatsby site.
Create config file for Jest
- Gatsby handles its own Babel config
- Must manually tell Jest to use
babel-jest
- Easiest way to do this is through a
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`],
}
transform
section tells Jest that js
and jsx
files need to be transformed using jest-preprocess.js
file in project root
const babelOptions = {
presets: ["babel-preset-gatsby"],
}
module.exports = require("babel-jest").createTransformer(babelOptions)
moduleNameMapper
tells Jest how to handle imports - Main concern here is mocking static file imports because Jest can't handle them. - The mock is a dummy module used instead of the real module inside of testsidentity-obj-proxy
is used to mock CSS stylesheets - For all other assets we use a manual mock called file-mock.js
in a __mocks__
dir at the project root - I'm not sure I understand what role this file actually plays. Seems like it's just exporting something that does nothing.
javascript module.exports = "test-file-stub"
testPathIgnorePatterns
tells Jest to ignore any tests in node_modules
or .cache
dirstransformIgnorePatterns
is required because Gatsby includes untranspiled ES6 code. By default Jest doesn't try to transform code inside node_modules
, so we'll get a SyntaxError
at runtime. - "This is because gatsby-browser-entry.js isn’t being transpiled before running in Jest. You can fix this by changing the default transformIgnorePatterns to exclude the gatsby module."globals
sets __PATH_PREFIX__
which is normally set by Gatsby, and is required by some components - I'm interested in which components require this.testURL
must be set if using <Jest 23.5 because some DOM APIs like localStorage
don't work well with the default value (about:blank
)setupFiles
array is a list of files that will be included before all tests are run. - We use loadershim.js
to include jest.fn()
on a global loader object?
global.___loader = {
enqueue: jest.fn(),
}
Mock Gatsby
const React = require("react")
const gatsby = jest.requireActual("gatsby")
module.exports = {
...gatsby,
graphql: jest.fn(),
Link: jest.fn().mockImplementation(
({
activeClassName,
activeStyle,
getProps,
innerRef,
partiallyActive,
ref,
replace,
to,
...rest
}) =>
React.createElement("a", {
...rest,
href: to,
})
),
StaticQuery: jest.fn(),
useStaticQuery: jest.fn(),
}
- This mocks the
graphql
function and the Link
and StaticQuery
components that are exportsrom the gatsby
package.
Writing Tests
- Can either use a __tests__ directory or colocate with the components - I tend to use a separate directory unless the project is very large and each component lives in its own directory.
- Use either the extension
spec.js
or .test.js
for test files
Example Snapshot Test
import React from "react"
import renderer from "react-test-renderer"
import Header from "../header"
describe("Header", () => {
it("renders correctly", () => {
const tree = renderer
.create(<Header siteTitle="Default Starter" />)
.toJSON()
expect(tree).toMatchSnapshot()
})
})
- This is a snapshot test using
react-test-renderer
to render the component - On first run, generates a snapshot of the component - Snapshots are generated in a __snapshots__ directory below our __tests__ directory
- On subsequent runs, compares future snapshots against the original - This allows us to quickly check for regressions and to quickly recognize changes that have occured with our ouput
- Snapshots are JSON representations of our components - For this reason, snapshots should be checked into VCS
- To update snapshots, run the test with a
-u
flag
Running Tests
- If it does not already exist (it should, by default) create a
test
script in package.json
"scripts": {
"test": "jest"
}
- Run
npm test --watch
to run Jest in watch mode - Run
npm test -u
to update snapshots
Using TypeScript
- Update the
transform
in jest.config.js
to target ts
and tsx
files as well - "^.+\\.[jt]sx?$": '<rootDir>/jest-preprocess.js'
- Update
jest.preprocess.js
with the @babel/preset-typescript
const babelOptions = {
presets: ["babel-preset-gatsby", "@babel/preset-typescript"],
}
Other Resources