Skip to content

测试规范

适用于所有包的单元测试和组件测试。 测试框架: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