Skip to main content

tsed-testing

Write unit and integration tests for Ts.ED v8 applications with Vitest (Jest as a fallback) - vitest.config with unplugin-swc, PlatformTest.create/invoke/reset from @tsed/platform-http/testing, provider mocking with {token, use}, inject() in specs, PlatformTest.createRequestContext, and PlatformTest.bootstrap(Server) with SuperTest and PlatformTest.callback(). Use when adding or fixing *.spec.ts files for services, controllers, middlewares or interceptors, when injected properties are undefined in tests, or on the error "Platform type is not specified" / a missing platform adapter.

来源信息

仓库
tsedio/tsed
最近来源活动
2026年10月3日 16:50
检测到的 SKILL.md 语言
英语
星标
3,089
分支
292

安装方式

默认使用会先检查来源的 Prompt;你也可以切换为直接命令,或下载本地副本。

检查来源文件

决定是否安装前,请先阅读 SKILL.md,以及 SkillsMP 当前展示的配套文件。

文件资源管理器
3 个文件

正在显示 SKILL.md

SKILL.md
来源说明 · 只读预览
name
tsed-testing
description
Write unit and integration tests for Ts.ED v8 applications with Vitest (Jest as a fallback) - vitest.config with unplugin-swc, PlatformTest.create/invoke/reset from @tsed/platform-http/testing, provider mocking with {token, use}, inject() in specs, PlatformTest.createRequestContext, and PlatformTest.bootstrap(Server) with SuperTest and PlatformTest.callback(). Use when adding or fixing *.spec.ts files for services, controllers, middlewares or interceptors, when injected properties are undefined in tests, or on the error "Platform type is not specified" / a missing platform adapter.
# Test a Ts.ED Application Build every tested class through the Ts.ED test injector. Never instantiate a DI-managed class (service, controller, middleware, interceptor, module) with `new` in a test: it bypasses injection, scopes, hooks and mocks. Read [the testing recipes](references/recipes.md) for middleware, interceptor, request-context and stubbing examples. Depth: `https://tsed.dev/docs/testing.md`, `https://tsed.dev/tutorials/vitest.md`. ## 1. Configure Vitest Install `vitest unplugin-swc @swc/core @vitest/coverage-v8` (and `supertest @types/supertest` for integration tests). A project generated by tsed-cli already has this. ```typescript // vitest.config.mts import swc from "unplugin-swc"; import {defineConfig} from "vitest/config"; export default defineConfig({ test: {globals: true, root: "./"}, plugins: [ swc.vite({ jsc: { target: "es2022", keepClassNames: true, parser: {syntax: "typescript", decorators: true}, transform: {useDefineForClassFields: false, legacyDecorator: true, decoratorMetadata: true} } }) ] }); ``` - Do not rely on Vitest's default esbuild transform: it does not emit decorator metadata, so constructor and `@Inject()` property injection resolve to `undefined`. - Do not remove `useDefineForClassFields: false`; class fields would overwrite injected properties. - Jest: use `ts-jest` per `https://tsed.dev/tutorials/jest.md`. It is unstable with ESM; prefer Vitest for new projects. ## 2. Choose the test type 1. Logic of one provider with mocked collaborators: unit test with `PlatformTest.create()` (steps 3-4). Fast, no HTTP server. 2. Routing, validation, serialization, middlewares chain, exception filters: integration test with `PlatformTest.bootstrap(Server)` + SuperTest (step 5). 3. Real database or broker: add a testcontainers package (step 6). ## 3. Unit test a provider ```typescript import {inject} from "@tsed/di"; import {PlatformTest} from "@tsed/platform-http/testing"; import {afterEach, beforeEach, describe, expect, it} from "vitest"; import {UsersService} from "./UsersService.js"; describe("UsersService", () => { beforeEach(() => PlatformTest.create()); afterEach(() => PlatformTest.reset()); it("should return the user", async () => { const service = inject(UsersService); expect(await service.findById("1")).toEqual({id: "1"}); }); }); ``` - `PlatformTest.create(settings?)` accepts any configuration key: `PlatformTest.create({features: {beta: true}})` then read it with `constant("features.beta")`. - `PlatformTest.get(Token)` and `inject(Token)` return the shared instance. `PlatformTest.injector` is the `InjectorService`. - Import `inject` from `@tsed/di`. `@tsed/platform-http/testing` only exports `PlatformTest` and `FakeAdapter`. - For code without HTTP dependencies, `DITest` from `@tsed/di` offers the same `create`, `invoke`, `get`, `reset`. ## 4. Mock dependencies ```typescript const repository = {findById: vi.fn().mockResolvedValue({id: "1"})}; const service = await PlatformTest.invoke<UsersService>(UsersService, [{token: UsersRepository, use: repository}]); expect(await service.findById("1")).toEqual({id: "1"}); expect(repository.findById).toHaveBeenCalledWith("1"); ``` - `PlatformTest.invoke(Token, [{token, use}])` builds a fresh instance with local mocks and runs `$onInit`. Always `await` it. - Mock for the whole `describe`: `PlatformTest.create({imports: [{token: UsersRepository, use: repository}]})`, then `inject(UsersService)`. - `imports` entries accept `use`, `useClass`, `useFactory` or `useAsyncFactory`. `useValue` is ignored there. - Symbol or factory tokens are mocked the same way: `{token: DbConnection, use: fakeDb}`. - Controllers are providers: `await PlatformTest.invoke<UsersController>(UsersController, [...])` and call the handler method directly with plain arguments. - Mock only direct collaborators. Do not `vi.mock()` a class file that the injector must still register. ## 5. Integration test with SuperTest ```typescript import {PlatformTest} from "@tsed/platform-http/testing"; import SuperTest from "supertest"; import {afterAll, beforeAll, describe, expect, it} from "vitest"; import {Server} from "../Server.js"; import {UsersController} from "./UsersController.js"; describe("UsersController", () => { beforeAll(PlatformTest.bootstrap(Server, {mount: {"/rest": [UsersController]}})); afterAll(PlatformTest.reset); it("should call GET /rest/users/:id", async () => { const request = SuperTest(PlatformTest.callback()); const response = await request.get("/rest/users/1").expect(200); expect(response.body).toEqual({id: "1"}); }); }); ``` - `PlatformTest.bootstrap()` returns a function; pass it to `beforeAll`, or call it: `await PlatformTest.bootstrap(Server)()`. - The platform adapter must be registered: import `@tsed/platform-express` (or `-koa`, `-fastify`) in `Server.ts` or in the spec, or pass `{adapter: PlatformExpress}`. - No port is opened unless `listen: true` is passed. The environment is `test` and the logger level is `off` by default. - Settings passed as second argument replace the same `@Configuration` keys (only `mount`, `scopes`, `logger` are merged). Passing `imports` drops the Server's own `imports`. - Stub after bootstrap: `vi.spyOn(PlatformTest.get(UsersService), "findById").mockResolvedValue(...)`. ## 6. Use real infrastructure sparingly Point to `@tsed/testcontainers-mongo` (`https://tsed.dev/tutorials/mongoose.md`) and the premium `@tsedio/testcontainers-*` packages (Postgres, Redis, Vault) listed in `https://tsed.dev/docs/testing.md`. Do not hand-roll container lifecycle code when a package exists. ## Pitfalls - "Platform type is not specified" (older releases) or a crash while creating the platform adapter in `PlatformTest.bootstrap`: no adapter package was imported. Add `import "@tsed/platform-express";`. - `beforeEach(PlatformTest.create)` passes Vitest's test context as settings. Write `beforeEach(() => PlatformTest.create())`. - Missing `PlatformTest.reset` leaks providers, hooks and mocks into the next file and keeps handles open. - `PlatformTest.invoke` without `await` returns a promise; assertions then run on the promise. - `PlatformTest.inject([Token], cb)` is deprecated. Use `inject()` or `PlatformTest.invoke()`. - Services reading `context()` need `runInContext(PlatformTest.createRequestContext(), () => ...)` from `@tsed/di`. - One `PlatformTest.bootstrap` per file in `beforeAll`, not `beforeEach`: bootstrapping the server per test is slow. - Do not import from `@tsed/common`; `PlatformTest` lives in `@tsed/platform-http/testing`. ## Checklist - `vitest.config` uses `unplugin-swc` with `decorators`, `legacyDecorator`, `decoratorMetadata` and `useDefineForClassFields: false`. - No `new` on a DI-managed class anywhere in the specs; instances come from `inject()`, `PlatformTest.get()` or `PlatformTest.invoke()`. - Every `PlatformTest.create`/`bootstrap` has a matching `PlatformTest.reset`. - Mocks use `{token, use}` and are asserted with `toHaveBeenCalledWith`; success and error paths are covered. - Integration specs import the platform adapter and build requests with `SuperTest(PlatformTest.callback())`. - Related skills: tsed-di (providers, scopes), tsed-configuration (settings overrides), tsed-controllers, tsed-middlewares, tsed-exceptions (error payload assertions).
在 GitHub 查看