单元和集成测试指南
¥Unit and integration testing guide
Page summary:
Testing relies on Jest and Supertest with an in-memory SQLite database, a patched Strapi test harness that also supports TypeScript configuration files, and helpers that automatically register the
/helloroute and authenticated role during setup.
本指南提供了在 Strapi 5 应用中配置 Jest 的实践方法,包括模拟 Strapi 对象以进行单元测试插件代码,以及使用 超测 端到端测试 REST 端点。
¥The present guide provides a hands-on approach to configuring Jest in a Strapi 5 application, mocking the Strapi object for unit testing plugin code, and using Supertest to test REST endpoints end to end.
本指南旨在重新创建 strapi-unit-testing-examples CodeSandbox 链接中提供的最小测试套件。
¥The guide aims to recreate the minimal test suite available in the strapi-unit-testing-examples CodeSandbox link.
如果你在 Windows 上使用 SQLite 数据库,则本指南将不起作用,因为 Windows 会锁定 SQLite 文件。
¥The present guide will not work if you are on Windows using the SQLite database due to how Windows locks the SQLite file.
安装工具
¥Install tools
我们将首先安装测试工具,添加运行测试的命令,并配置 Jest。
¥We'll first install test tools, add a command to run our tests, and configure Jest.
-
通过在终端中运行以下命令安装 Jest 和 Supertest:
¥Install Jest and Supertest by running the following command in a terminal:
- Yarn
- NPM
```bash
yarn add jest supertest --dev
```
```bash
npm install jest supertest --save-dev
```
* `Jest` provides the test runner and assertion utilities.
* `Supertest` allows you to test all the `api` routes as they were instances of <ExternalLink to="https://nodejs.cn/api/http.html#class-httpserver" text="http.Server"/>.
* Add a `test` command to the `scripts` section so it looks as follows:
"scripts": {
"build": "strapi build",
"console": "strapi console",
"deploy": "strapi deploy",
"dev": "strapi develop",
"develop": "strapi develop",
"seed:example": "node ./scripts/seed.js",
"start": "strapi start",
"strapi": "strapi",
"upgrade": "npx @strapi/upgrade latest",
"upgrade:dry": "npx @strapi/upgrade latest --dry",
"test": "jest --forceExit --detectOpenHandles"
},
* Configure Jest at the bottom of the file to ignore Strapi build artifacts and to map any root-level modules you import from tests:
"jest": {
"testPathIgnorePatterns": [
"/node_modules/",
".tmp",
".cache"
],
"testEnvironment": "node",
"moduleNameMapper": {
"^/create-service$": "<rootDir>/create-service"
}
}
模拟 Strapi 进行插件单元测试
¥Mock Strapi for plugin unit tests
纯单元测试非常适合 Strapi 插件,因为它们允许你在不启动 Strapi 服务器的情况下验证控制器和服务逻辑。使用 Jest 的 mocking
¥Pure unit tests are ideal for Strapi plugins because they let you validate controller and service logic without starting a Strapi server. Use Jest's mocking
用于重新创建 Strapi 对象的各个部分以及代码所依赖的任何请求上下文的实用程序。
¥utilities to recreate just the parts of the Strapi object and any request context that your code relies on.
控制器示例
¥Controller example
创建一个测试文件,例如 ./tests/todo-controller.test.js,使用模拟的 Strapi 对象实例化你的控制器,并验证控制器执行的每个调用:
¥Create a test file such as ./tests/todo-controller.test.js that instantiates your controller with a mocked Strapi object and verifies every call the controller performs:
const todoController = require('./todo-controller');
describe('Todo controller', () => {
let strapi;
beforeEach(() => {
strapi = {
plugin: jest.fn().mockReturnValue({
service: jest.fn().mockReturnValue({
create: jest.fn().mockReturnValue({
data: {
name: 'test',
status: false,
},
}),
complete: jest.fn().mockReturnValue({
data: {
id: 1,
status: true,
},
}),
}),
}),
};
});
it('creates a todo item', async () => {
const ctx = {
request: {
body: {
name: 'test',
},
},
body: null,
};
await todoController({ strapi }).index(ctx);
expect(ctx.body).toBe('created');
expect(strapi.plugin('todo').service('create').create).toHaveBeenCalledTimes(1);
});
it('completes a todo item', async () => {
const ctx = {
request: {
body: {
id: 1,
},
},
body: null,
};
await todoController({ strapi }).complete(ctx);
expect(ctx.body).toBe('todo completed');
expect(strapi.plugin('todo').service('complete').complete).toHaveBeenCalledTimes(1);
});
});
beforeEach 钩子会重建模拟,因此每个测试都从一个干净的 Strapi 实例开始。每个测试都会准备控制器期望的 ctx 请求对象,调用控制器函数,并断言响应和与 Strapi 服务的交互。
¥The beforeEach hook rebuilds the mock so every test starts with a clean Strapi instance. Each test prepares the ctx request object that the controller expects, calls the controller function, and asserts both the response and the interactions with Strapi services.
服务示例
¥Service example
服务可以在同一个测试套件中进行测试,也可以在专用文件中通过仅模拟它们调用的 Strapi 查询层进行测试。
¥Services can be tested in the same test suite or in a dedicated file by mocking only the Strapi query layer they call into.
const createService = require('./create-service');
describe('Create service', () => {
let strapi;
beforeEach(() => {
strapi = {
query: jest.fn().mockReturnValue({
create: jest.fn().mockReturnValue({
data: {
name: 'test',
status: false,
},
}),
}),
};
});
it('persists a todo item', async () => {
const todo = await createService({ strapi }).create({ name: 'test' });
expect(strapi.query('plugin::todo.todo').create).toHaveBeenCalledTimes(1);
expect(todo.data.name).toBe('test');
});
});
通过专注于 mocking 代码接触到的特定 Strapi API,你可以扩展这些测试以覆盖更多分支、错误情况和服务,同时保持它们的快速和隔离。
¥By focusing on mocking the specific Strapi APIs your code touches, you can grow these tests to cover additional branches, error cases, and services while keeping them fast and isolated.
设置测试环境
¥Set up a testing environment
对于使用 超测 进行 API 级测试,框架必须具有干净的空环境才能执行有效测试,并且不会干扰你的开发数据库。
¥For API-level testing with Supertest , the framework must have a clean empty environment to perform valid tests and also not to interfere with your development database.
jest 运行时会使用 test environment,因此请使用以下命令创建 ./config/env/test/database.js:
¥Once jest is running it uses the test environment, so create ./config/env/test/database.js with the following:
module.exports = ({ env }) => {
const filename = env('DATABASE_FILENAME', '.tmp/test.db');
const rawClient = env('DATABASE_CLIENT', 'sqlite');
const client = ['sqlite3', 'better-sqlite3'].includes(rawClient) ? 'sqlite' : rawClient;
return {
connection: {
client,
connection: {
filename,
},
useNullAsDefault: true,
},
};
};
此配置镜像了生产环境中使用的默认设置,但将 better-sqlite3 转换为 Strapi 期望的 sqlite 客户端。
¥This configuration mirrors the defaults used in production but converts better-sqlite3 to the sqlite client Strapi expects.
Dist directory and multiple database configurations:
在本地开发时,你可能同时拥有项目级 config/database.(ts|js) 和特定于环境的 config/env/test/database.js。
¥When developing locally you might have both a project-level config/database.(ts|js) and an environment-specific config/env/test/database.js.
如果你在开发环境中运行应用(例如 yarn dev),Strapi 会将配置编译到 dist/config 中。如果你的测试强制 Strapi 从 dist 读取(例如,通过传递 createStrapi({ appDir: './', distDir: './dist' })),则只有一个数据库配置最终会进入 dist/config/database.js。这可能导致 Jest 在开发构建后获取错误的数据库设置。
¥If you run the app in development (e.g., yarn dev) Strapi compiles configurations into dist/config. If your tests then force Strapi to read from dist (e.g., by passing createStrapi({ appDir: './', distDir: './dist' })), only one database config may end up in dist/config/database.js. This can cause Jest to pick up the wrong DB settings after a dev build.
建议:
¥Recommendations:
-
请勿在测试工具中传递自定义
distDir;让 Strapi 直接从源代码加载。本指南中的工具链调用createStrapi().load()时无需覆盖,从而避免了冲突。¥Do not pass a custom
distDirin the test harness; let Strapi load directly from source. The harness in this guide callscreateStrapi().load()without overrides, which prevents the conflict. -
Jest 始终依赖
config/env/test/database.js。避免在yarn test之前立即运行yarn dev。如果存在这种情况,请考虑移除dist/或直接运行测试而不强制使用distDir。¥Always rely on
config/env/test/database.jsfor Jest. Avoid runningyarn devimmediately beforeyarn test. If you did, consider removingdist/or simply run tests without forcingdistDir. -
如果你必须使用
dist/,请确保其config/database.js与你的测试环境匹配,或者专门为测试清理/重建。¥If you must use
dist/, ensure itsconfig/database.jsmatches your test environment, or clean/rebuild specifically for tests.
创建 Strapi 测试工具
¥Create the Strapi test harness
我们将在项目根目录中创建一个 tests 文件夹,并添加以下示例文件。这 3 个文件协同工作,以创建一个完整的测试基础架构:
¥We will create a tests folder in your project root and add the example files below. These 3 files work together to create a complete testing infrastructure:
-
ts-compiler-options.js定义了 TypeScript 文件应如何编译以进行测试。¥
ts-compiler-options.jsdefines how TypeScript files should be compiled for testing -
ts-runtime.js使 Jest 能够动态理解和执行 TypeScript 文件。¥
ts-runtime.jsenables Jest to understand and execute TypeScript files on the fly -
strapi.js是主要的测试工具。