This is a example of a Gadget app with unit tests.
Vitest is used as a unit test runner because it works nicely with Vite, which is used to power Gadget app frontends.
You can fork this project to try it out yourself.
The following packages have been installed, and can be found in this project's package.json
:
vitest
: the test runner, fully compatible with Vitedotenv
: used to import environment variables defined in a.env.local
react-test-renderer
: used for snapshot testing
A test
folder has been added to the project root. This folder contains sample test files for:
- model actions: see
test/post/actions/create.test.js
- util functions: see
test/post/utils/getWordCount.js
- HTTP routes: see
test/routes/GET-hello.test.js
- frontends (snapshot testing): see
test/frontend/routes/index.test.jsx
for a simple example andtest/frontend/routes/signed-in.test.jsx
for an example with API client and React hook mocks
- Use FileSync to pull your Gadget project's code files to your local machine
- Install the required packages (
yarn add vitest dotenv react-test-renderer
) - Add a
test
script topackage.json
"scripts": {
"vite:build": "NODE_ENV=production vite build",
"test": "vitest"
}
- Modify your
vite.config.js
to includetest
config for vitest
/// <reference types="vitest" />
import react from "@vitejs/plugin-react-swc";
import { defineConfig } from "vite";
// set up test config to read from .env.local
import dotenv from "dotenv";
dotenv.config({ path: __dirname + "/.env.local" });
export default defineConfig({
plugins: [react()],
clearScreen: false,
test: {
// this test config is added to the defineConfig function so env vars inside .env.local are available
setupFiles: ["dotenv/config"],
// clear mocks between tests
mockReset: true,
},
});
- Create a
.ignore
file at the root-level of your Gadget project (this prevents local files from being synced to the Gadget editor with FileSync) - Add
.env.local
to your.ignore
file - Create a
.env.local
file at the root-level of your Gadget project - In the Gadget web editor, go to the API key page (Settings -> API Keys) and create a new key (assign it the role you wish to test)
- Copy the API key for the Development environment and add it as an environment variable in your local
.env.local
file
// example entry in .env.local
GADGET_TEST_API_KEY=gsk-12121212121212121212121212121212
- Create a test API client for the Development environment somewhere in your project (this sample project uses
test/api.js
)
// make sure to replace "@gadget-client/unit-test-sample" with your Gadget API client!
import { Client } from "@gadget-client/unit-test-sample";
// an API client created using the GADGET_TEST_API_KEY environment variable
export const api = new Client({
environment: "Development",
authenticationMode: {
apiKey: process.env.GADGET_TEST_API_KEY,
},
});
// an API client created without an API key will have the unauthenticated role
export const unauthenticatedApi = new Client({ environment: "Development" });
- Import an API client into one of your test files to test out model actions, global actions, and HTTP routes.
A sample GitHub action file can be found in .github/workflows/run_tests.yml
.
This action runs tests on every push and pull request to merge to the main
branch, installs dependencies using yarn
and runs tests using yarn test
.
You should also create a .gitignore
file if you are using GitHub to prevent the pushing of your .env.local
file and node_modules
to GitHub.
// sample .gitignore file
.env.local
.gadget/sync.json
node_modules
You need to pass in a Gadget API key as a secret so that the API client in tests can be initialized. Google has some documentation on setting up and using encrypted secrets.
For this example, a repository secret was created containing the Gadget API key environment variable definition (ie. copy all of the contents of your .env.local
file). The secret was then passed into the GitHub action using the env
key in the action file.
- name: Create env file
run: |
echo "${{ secrets.GADGET_TEST_API_KEY }}" > .env
This app is built using the Web app starter template.
A sample post
model has been added to this app to demonstrate testing model actions in Gadget.
post
: stores blog post records with the following fieldstitle
is the blog post title (string field)content
is the content of the blog post (rich text field)wordCount
is the count of words in a post (number field)isPublished
whether or not the post has been publicly published (boolean field)user
the author of the post (belongs-to relationship)
user
- keeps track of all users who have signed up
- added relationship to
post
model so thatuser
has manyposts
session
- keeps track of user sessions
No environment variables have been added to the Gadget project (Settings -> Environment Variables in the Gadget editor).
A .env.local file is required for this project, which contains a GADGET_TEST_API_KEY=your-api-key
environment variable used to initialized test API clients found in test/api.js
.
A post
model has been added to this project to provide an example of testing Gadget actions and standalone util functions
post/actions/create.js
: custom code added to therun
function to calculate the word count- tests found in
test/post/actions/create.test.js
- tests found in
post/utils/getWordCount.js
: a simple util to calculate the word count of an input string- tests found in
test/post/utils/getWordCount.test.js
- tests found in
The template's default GET-hello.js
HTTP route is used to illustrate the testing of an HTTP route.
routes/GET-hello.js
returns a{hello: "world"}
object- test found in
test/routes/GET-hello.test.js
- test found in
A custom API key for this project was created with the system-admin
role. This API key is used as an environment variable inside a .env.local
file and powers the test API clients.
This app uses the Web app template's standard frontend, with a minor addition to frontend/routes/signed-in.jsx
to demonstrate the mocking of a useFindMany hook.
Key files:
frontend/routes/index.jsx
the default page, similar to a simple React component- snapshot test found in
test/frontend/routes/index.test.jsx
- snapshot test found in
frontend/routes/signed-in.jsx
the page for a signed-in user- snapshot tests found in
test/frontend/route/signed-in.jsx
- snapshot tests found in
Once you have your .env.local environment variables and API client set up, you can test your Gadget project like you would any other Node/React project.
Join our developer Discord if you have any questions about this sample or writing unit tests in Gadget!