mirror of https://gitee.com/antv-l7/antv-l7
143 lines
5.2 KiB
Markdown
143 lines
5.2 KiB
Markdown
|
# 自动化测试方案
|
|||
|
|
|||
|
如何测试一个 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 API,DOM 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。
|