Skip to content

测试与检查 ​

每次改动之后,有两个工具帮你闭环: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 码
decoratorstsconfig.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 协作。