测试与检查
每次改动之后,有两个工具帮你闭环:cell check 不运行应用就能发现错误,createTestApp() 在测试进程里运行应用。
cell check
bash
npx cell check # 有错误时退出码为 1
npx cell check --json # 一个 JSON 文档:passed、summary、issues| 检查 | 发现的问题 |
|---|---|
config | 缺少入口、未知的 -m mode、匹配不到任何键的 CELL_* 环境变量 |
types | 项目的 tsc --noEmit,每条诊断带文件、行号和 TSxxxx 码 |
decorators | tsconfig.json 缺少 experimentalDecorators / emitDecoratorMetadata |
di | 不启动应用、加载后端容器并逐个解析绑定:CELL_DI_NO_BINDING、CELL_DI_AMBIGUOUS、CELL_DI_CIRCULAR…… |
每个问题都有 code、已知时的 file 和 line,以及 hint:一句话的修复建议。--skip <check> 跳过某项;组件可以注册自己的检查(例如 typeorm:entities)。
createTestApp()
ts
// test/users.spec.ts
import { afterAll, beforeAll, describe, expect, it } from 'bun:test';
import { join } from 'node:path';
import { createTestApp, type TestApp } from '@celljs/cli';
describe('users', () => {
let app: TestApp;
beforeAll(async () => {
app = await createTestApp({ root: join(__dirname, '..') });
}, 60000); // 第一次启动要编译源码
afterAll(() => app?.close()); // 停止应用:触发 onStop
it('creates a user over HTTP', async () => {
const res = await app.fetch('/users', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ name: 'Ada' }),
});
expect(res.status).toBe(200);
expect(await res.json()).toMatchObject({ name: 'Ada' });
});
it('calls a service directly', async () => {
const { UserService } = await app.import('/src/node/user-service.ts'); // 应用加载的那份模块
expect(await app.get<any>(UserService).count()).toBe(1);
});
});- 后端按
cell dev的方式从源码加载:不构建,也不监听端口。fetch(path, init)让请求走完整个 HTTP 处理链(中间件、安全、控制器、错误处理器),返回标准的Response。 app.get(Id)返回应用里的组件,app.container.getAll(Id)返回全部。应用运行在自己的模块实例里:类要从await app.import('/src/node/user-service.ts')取(包写包名),不要在测试文件里直接import。测试自己导入的类或Symbol按名字匹配,对 symbol 和没有注入的类有效,但带@Value/@Autowired的类导入到测试进程后会报CELL_DI_NO_BINDING。- 生效的 mode 是
test:测试专用的配置写在cell.test.yml,例如内存数据库。createTestApp({ mode: ['seed'] })在test之后追加 mode。 - 启动失败时抛出
CellError(CELL_DEV_BACKEND_FAILED,带出错的文件)。没有 HTTP 后端时fetch抛出CELL_TEST_APP_NO_HTTP。
用 bun test 运行测试(模板的 npm test 是 bun test ./test)。每个测试文件一个应用;同一文件里的测试共享状态,所以要么让它们与顺序无关,要么在 beforeEach 里重置数据。
示例:测试数据库
yaml
# cell.test.yml
typeorm:
ormConfig:
- type: sqljs # 内存数据库,来自 sql.js 包
synchronize: true不启动应用的单元测试
普通的类可以直接 new,手动传入协作对象。用属性注入的组件需要容器:优先用 createTestApp(),而不是自己组装容器。
调试运行中的应用
cell dev 运行时,同一端口上的 POST /_cell/mcp 是给 coding agent 用的 MCP 服务器,提供 get_errors(启动错误、失败的重载、5xx 请求、错误日志)、list_routes、list_bindings、get_config、recent_requests 和 logs。见与 Coding Agent 协作。