Appearance
测试规范
适用于所有包的单元测试和组件测试。 测试框架:Vitest 3 + happy-dom + @vue/test-utils。
测试文件组织
src/lib/core/<module>/
├── index.ts
├── types.ts
├── XxxEffect.ts
└── __tests__/
└── XxxEffect.spec.ts # 与被测文件同名 + .spec.ts- 测试文件放在被测模块的
__tests__/子目录 - 命名:
<被测文件名>.spec.ts - 一个 spec 文件对应一个源文件
编写规则
必须遵守
- 使用
describe/it组织,名称用中文 - 语义化断言:
expect(effect.source).toBe(entity)优于expect(obj._source).toBe(entity) - 断言失败信息必须可读:
expect(value, '销毁后引用应为 null').toBeNull() - 测试不依赖其他测试的副作用:每个
it独立 - 模拟外部依赖:对 Cesium Viewer/Entity/EntityCollection 使用最小化 mock
禁止事项
- ❌
expect(true).toBe(true)类无意义断言 - ❌ 测试依赖执行顺序
- ❌ 使用
setTimeout/waitForTimeout硬等 - ❌ 在测试中引入真实的 Cesium Viewer(太重,应 mock)
测试模式
Effect 类测试模板
typescript
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
import type * as Cesium from 'cesium'
import { XxxEffect } from '../XxxEffect'
import { createMockViewer } from '../../__tests__/mockViewer'
describe('XxxEffect', () => {
let effect: XxxEffect
let viewer: Cesium.Viewer
beforeEach(() => {
viewer = createMockViewer().viewer
effect = new XxxEffect({ /* 最小必填参数 */ })
})
afterEach(() => {
effect.destroy(viewer)
})
it('构造函数应正确初始化必填参数', () => { ... })
it('构造函数应对可选参数使用默认值', () => { ... })
it('on() 应启用效果', () => { ... })
it('on() 重复调用不应创建重复资源', () => { ... })
it('off() 应禁用效果', () => { ... })
it('off() 未启用时调用不应报错', () => { ... })
it('destroy() 应释放所有引用', () => { ... })
})Plugin 类测试模板
typescript
import { describe, it, expect, beforeEach } from 'vitest'
import type * as Cesium from 'cesium'
import { viewerXxxMixin } from '../viewerXxxMixin'
import { extendViewer, runViewerDestroyHooks } from '../extend'
import { createMockViewer } from '../../__tests__/mockViewer'
describe('viewerXxxMixin', () => {
let viewer: Cesium.Viewer
beforeEach(() => {
viewer = createMockViewer().viewer
extendViewer(viewer, viewerXxxMixin)
})
it('应在 viewer 上挂载 API', () => {
expect('xxx' in viewer).toBe(true)
})
it('销毁钩子应释放 mixin 创建的资源', () => {
runViewerDestroyHooks(viewer)
// 断言事件、DOM、Entity/Primitive 等自有资源已释放
})
})Material 类测试模板
typescript
import { describe, it, expect } from 'vitest'
import { XxxMaterialProperty } from '../XxxMaterialProperty'
describe('XxxMaterialProperty', () => {
it('getType() 应返回正确类型名', () => { ... })
it('getValue() 应返回当前值', () => { ... })
it('equals() 相同参数应返回 true', () => { ... })
it('equals() 不同参数应返回 false', () => { ... })
})Tool/Util 函数测试模板
typescript
import { describe, it, expect } from 'vitest'
import { xxxUtil } from '../xxxUtil'
describe('xxxUtil', () => {
it('正常输入应返回预期结果', () => { ... })
it('边界值输入应正确处理', () => { ... })
it('异常输入应抛出错误', () => { ... })
})覆盖要求
| 模块类型 | 最低覆盖 | 关键覆盖点 |
|---|---|---|
| effect | 公共 API 全覆盖 | on/off/destroy 生命周期、参数校验 |
| plugin | 公共 API 全覆盖 | viewer.extend 注入、API 挂载、destroy |
| material | 公共 API 全覆盖 | constructor/getType/getValue/equals |
| tool/util | 核心逻辑全覆盖 | 正常/边界/异常输入 |
| entity | 核心逻辑全覆盖 | 创建的 Entity 结构验证 |
运行测试
bash
# 根目录运行所有测试
pnpm test:all
# 针对单个包
pnpm --filter @nexa/gis-cesium test
# 针对单个文件
pnpm vitest run packages/gis-cesium/src/lib/core/effects/__tests__/Xxx.spec.ts
# watch 模式
pnpm --filter @nexa/gis-cesium test:watch