antv-l7/dev-docs/自动化测试方案.md

143 lines
5.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 自动化测试方案
如何测试一个 WebGL 应用渲染结果是否正确呢?常用的做法是进行像素比对,当然这也只能用于一些简单的判断例如渲染是否成功,整体的 Snapshot 比对开销很大。
但不管怎么说,这都意味着我们必须使用 WebGL API 进行真实的渲染。
以上过程在测试用例中描述如下:
```javascript
// 1. 绘制
// 2. 读取像素
const pixels = new Uint8Array(width * height * 4);
gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
// 3. 判断某个像素点是否符合预期
```
在与测试框架结合时,常用的方案有:
* Electron 除了 WebGL APIDOM API 等其他浏览器实现对于 WebGL 测试都是多余的,在 CI 时需要安装的依赖过大,所需的启动时间也很长。
* [node-webgl](https://github.com/mikeseven/node-webgl) 不同于 WebGL可以直接调用 OpenGL 驱动,但同样包含了很多 WebGL 之外的特性。
这里我们选择 [headless-gl](https://github.com/stackgl/headless-gl),一个纯粹的 WebGL 1 规范的实现。并且能够很容易集成进现有的 [CI 流程](https://github.com/stackgl/headless-gl#how-can-i-use-headless-gl-with-a-continuous-integration-service)中,例如 [TravisCI](https://travis-ci.org/) 和 [AppVeyor](http://www.appveyor.com/)。
## 测试框架
在配置测试框架前,我们必须解决一个 WebGL 项目中常见的问题。
### 引入 GLSL 文件
如何在测试时正确引入 GLSL 文件是一个问题。目前各个 3D 引擎常用的做法有两种:
* 以字符串形式直接写在 `.js` 文件中。`luma.gl/deck.gl` 使用[这种方式](https://github.com/uber/deck.gl/blob/7.1-release/modules/layers/src/arc-layer/arc-layer-fragment.glsl.js)。
* 使用 `.glsl` 编写,测试前使用构建脚本自动生成对应的 `.js` 文件。`Three.js`、`clay.gl` 使用[这种方式](https://github.com/pissang/claygl/blob/master/build/glsl2js.js)。
前者的好处是测试流程无需做过多修改,坏处则是无法享受编辑器对于 GLSL 的语法高亮,影响开发体验。而后者又需要编写额外的 `glsl2js` 的转换脚本。
我们显然希望如后者一样直接写 GLSL但最好能让测试框架帮助我们完成自动转换的工作。
之前我们选择 `@babel/preset-typescript` 而非官方 `tsc` 的一大原因就是可以使用 `babel` 丰富的插件,`babel-plugin-inline-import` 就能完成类似 webpack 中 `raw-loader` 的功能,直接以字符串形式引入 GLSL 代码:
```javascript
// 以字符串形式引入 GLSL 代码
import circleFrag from '../../shaders/circle_frag.glsl';
```
这样测试框架只需要使用同一套 babel 项目全局配置就行了。我们使用 Jest
```javascript
// jest.config.js
module.exports = {
transform: {
'^.+\\.(ts|tsx)$': 'babel-jest',
},
}
```
下面就可以编写测试用例了。
## 测试用例编写
我们将测试用例分成三类:
* 内部服务的单元测试
* 渲染服务结果的 Snapshot 快照测试
* React 组件测试
将测试用例放在任意路径的 `__tests__` 文件夹下并以 `xxx.spec.ts` 命名就可以帮助框架发现并执行了。
使用 `yarn test` 运行所有测试用例:
![](./screenshots/jest.png)
### 单元测试
这类测试直接使用 Jest API 就好了,我们以 `@l7/core` 模块的 `ShaderModuleService` 为例,编写一个简单的测试用例:
```typescript
// services/shader/__test__/shader.spec.ts
import 'reflect-metadata';
import IShaderModuleService from '../IShaderModuleService';
import ShaderModuleService from '../ShaderModuleService';
describe('ShaderService', () => {
let shaderService: IShaderModuleService;
beforeEach(() => {
shaderService = new ShaderModuleService();
});
it('should register common module correctly and generate fragment/vertex shader code', () => {
const rawShaderCode = `
#define PI 3.14
`;
const commonModule = {
fs: rawShaderCode,
vs: rawShaderCode,
};
shaderService.registerModule('common', commonModule);
const { vs, fs } = shaderService.getModule('common');
expect(vs).toMatch(/3\.14/);
expect(fs).toMatch(/3\.14/);
});
});
```
### 渲染结果测试
得益于 L7 使用的基于 Inversify 的依赖注入方案,我们能够很轻易地将渲染服务替换为基于 headless-gl 的渲染服务。
具体到我们目前的渲染服务实现 `regl`,它能轻易做到这一点。事实上 regl 的[测试用例](https://github.com/regl-project/regl/blob/gh-pages/test/util/create-context.js#L28)也是这样使用的。
### [WIP] React 组件测试
### Coverage Report
我们使用 Coveralls.io
```json
// package.json
"coveralls": "jest --coverage && cat ./tests/coverage/lcov.info | coveralls",
```
运行 `yarn coveralls` 可以查看代码覆盖率,我们为分支、行覆盖率等指标设置了阈值:
```javascript
// jest.config.js
module.exports = {
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80,
},
},
};
```
## TravisCI
TravisCI 检测到 `yarn.lock` 就会默认安装 `yarn` 并使用它安装依赖,所以不需要[额外的配置](https://yarnpkg.com/en/docs/install-ci#travis-tab)。
TravisCI 配合之前的 Coveralls.io。