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

5.3 KiB
Raw Blame History

自动化测试方案

如何测试一个 WebGL 应用渲染结果是否正确呢?常用的做法是进行像素比对,当然这也只能用于一些简单的判断例如渲染是否成功,整体的 Snapshot 比对开销很大。

但不管怎么说,这都意味着我们必须使用 WebGL API 进行真实的渲染。

以上过程在测试用例中描述如下:

// 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 不同于 WebGL可以直接调用 OpenGL 驱动,但同样包含了很多 WebGL 之外的特性。

这里我们选择 headless-gl,一个纯粹的 WebGL 1 规范的实现。并且能够很容易集成进现有的 CI 流程中,例如 TravisCIAppVeyor

测试框架

在配置测试框架前,我们必须解决一个 WebGL 项目中常见的问题。

引入 GLSL 文件

如何在测试时正确引入 GLSL 文件是一个问题。目前各个 3D 引擎常用的做法有两种:

  • 以字符串形式直接写在 .js 文件中。luma.gl/deck.gl 使用这种方式
  • 使用 .glsl 编写,测试前使用构建脚本自动生成对应的 .js 文件。Three.jsclay.gl 使用这种方式

前者的好处是测试流程无需做过多修改,坏处则是无法享受编辑器对于 GLSL 的语法高亮,影响开发体验。而后者又需要编写额外的 glsl2js 的转换脚本。

我们显然希望如后者一样直接写 GLSL但最好能让测试框架帮助我们完成自动转换的工作。

之前我们选择 @babel/preset-typescript 而非官方 tsc 的一大原因就是可以使用 babel 丰富的插件,babel-plugin-inline-import 就能完成类似 webpack 中 raw-loader 的功能,直接以字符串形式引入 GLSL 代码:

// 以字符串形式引入 GLSL 代码
import circleFrag from '../../shaders/circle_frag.glsl';

这样测试框架只需要使用同一套 babel 项目全局配置就行了。我们使用 Jest

// jest.config.js
module.exports = {
  transform: {
    '^.+\\.(ts|tsx)$': 'babel-jest',
  },
}

下面就可以编写测试用例了。

测试用例编写

我们将测试用例分成三类:

  • 内部服务的单元测试
  • 渲染服务结果的 Snapshot 快照测试
  • React 组件测试

将测试用例放在任意路径的 __tests__ 文件夹下并以 xxx.spec.ts 命名就可以帮助框架发现并执行了。

使用 yarn test 运行所有测试用例:

单元测试

这类测试直接使用 Jest API 就好了,我们以 @antv/l7-core 模块的 ShaderModuleService 为例,编写一个简单的测试用例:

// 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 的测试用例也是这样使用的。

[WIP] React 组件测试

Coverage Report

我们使用 Coveralls.io

// package.json
"coveralls": "jest --coverage && cat ./tests/coverage/lcov.info | coveralls",

运行 yarn coveralls 可以查看代码覆盖率,我们为分支、行覆盖率等指标设置了阈值:

// jest.config.js
module.exports = {
  coverageThreshold: {
    global: {
      branches: 80,
      functions: 80,
      lines: 80,
      statements: 80,
    },
  },
};

TravisCI

TravisCI 检测到 yarn.lock 就会默认安装 yarn 并使用它安装依赖,所以不需要额外的配置

TravisCI 配合之前的 Coveralls.io。