从0开发3D引擎(四):搭建测试环境
大家好,本文介绍了3D引擎的测试方法,搭建了本地的测试环境。
大家好,本文介绍了3D引擎的测试方法,搭建了本地的测试环境。
上一篇博文
下一篇博文
了解自动化测试
对于引擎开发这种复杂、长期的项目,为了减少bug,提升长期的开发效率,自动化测试必不可少。在我们的Wonder.js引擎中,包括了本节介绍的3种自动化测试,测试覆盖率达到了95%。
本系列为了节省篇幅,不进行自动化测试。因此本节只进行简单的介绍,不给出实际的案例,读者可以到Wonder.js->test/目录下查看自动化测试实例。
单元测试
我们需要写测试用例对单个函数进行单元测试。
搭建环境
使用jest作为测试框架,sinon进行stub。
如果读者想了解stub的概念,可以参考我对Stub和Mock的理解
因为不能直接使用js库,需要写对应的FFI(类似于typescript的d.ts文件)才能在Reason中被调用,所以我们可以使用bs-jest和Wonder的Wonder-bs-sinon作为FFI
集成测试
相对于单元测试,集成测试的测试目标变为某个特性,该特性跨越多个函数或多个模块。
搭建环境
与单元测试的环境一样。
目录结构
可以在test/unit/目录下写单元测试用例,而在test/integration/目录下写集成测试用例。
端对端测试
也称为e2e测试,包括了“渲染测试”和“性能测试”。它们都需要安装puppeteer,通过chrome内核渲染3D场景来进行测试。
- 渲染测试
渲染测试是针对特定的场景(如只有一个模型的场景,或者只有一个光源的场景)进行测试,从而保证渲染的正确性。
测试步骤为:
1、预先渲染一张正确图片
2、使用引擎渲染一张当前图片
3、逐个像素地比较两者,如果95%以上的像素都相同,则测试通过;否则测试失败
- 性能测试
性能测试是针对极端场景(如5000个box)进行测试,从而保证花费的时间和占用的内存大小符合要求。
测试步骤为:
1、预先准备基准数据
使用引擎运行场景多次,取平均值,记录到json文件中
2、使用引擎运行场景多次(少于“准备基准数据”的运行次数),取平均值,得到当前数据
3、比较两者的花费的时间和占用的内存大小,如果在误差范围内,则测试通过;否则测试失败
注意事项:
只有在本地测试时,保持基准数据不变。如果在云端(如在push到Github仓库时使用CI工具-travis进行测试)或者其它环境(如换一台电脑进行测试)进行性能测试,需要在每次测试时更新基准数据(因为不同的环境,性能不一样,所以对应的基准数据也不一样)。
通过打印日志来调试
有以下的原因使得可以在单元测试和集成测试中通过打印日志来进行调试:
- 因为在自动化测试中打开watch,代码修改后能够立即刷新,所以能够即时看到打印的结果,测试很方便
- 因为在函数式编程中,函数为纯函数(Reason也允许非纯),没有状态,所以我们可以通过打印函数的输入和输出,来验证该函数是否正确
了解运行测试
本系列通过在Chrome浏览器中进行运行测试来验证程序的正确性。
通过以下的方式进行运行测试:
断点调试
因为浏览器运行的是Reason编译后的js代码,所以我们可以在浏览器的控制台->Sources中通过断点来调试js代码
具体可以参考:
使用断点暂停代码
通过Spector.js测试WebGL
Spector.js调试预览:
Spector.js能查看一帧中WebGL的调用情况、shader代码和WebGL的状态,它支持WebGL 1.0或WebGL 2.0,也支持WebGL 1.0的VAO等扩展。
另外,Spector.js支持多个canvas,能查看指定的canvas对应的WebGL信息。这对于调试编辑器(如我们的Wonder-Editor在线编辑器)很有用。因为编辑器有多个canvas(如一个canvas进行主场景绘制,另一个canvas以材质球的方式显示单个material资产的效果),而我们希望分别调试从每个canvas中取得上下文的WebGL。
Spector.js可以在Chrome的扩展中安装,详情请见官方Github
通过log调试Shader
本系列通过在fragment shader中,将变量作为输出的颜色,来调试Shader的变量值。
更多可以参考:
调试OpenGL -> 调试着色器输出
OpenGL ES 2.0 Shader 调试新思路(一): 改变提问方式
移动端测试
我们通过下面两种方法进行测试:
- 模拟测试
我们可以在Chrome浏览器上,点击控制台->Toggle Device Toolbar,打开用于模拟移动设备视口的界面。
本系列主要用该方法测试引擎对于移动端touch事件的支持。
详细的介绍参考:
使用 Chrome DevTools 中的 Device Mode 模拟移动设备
- 真机测试
具体步骤如下:
1、在测试html页面中引入vConsole库
从而可以通过打印日志的方式,在手机上查看错误和日志信息
vConsole介绍参考:
前端开发 – 在手机上调试利器vConsole
2、把测试页面push到测试环境的服务器上(如使用Github Pages搭建的服务器)
3、把测试页面的访问地址转换为二维码
如使用草料二维码在线转换
4、用测试手机的微信扫该二维码,运行测试页面,验证渲染结果,查看错误和日志信息
了解性能测试
因为本系列开发的引擎重视性能,所以会通过手动的性能测试,来指导引擎优化。
性能测试的指标包括时间开销和内存开销,下面分别分析:
测试时间开销
- 使用Chrome DevTools的Javascript Profiler
通过在测试页面记录profile,查看每个函数的时间开销,从而定位到热点函数进行优化。
相关资料可参考:
加速执行 JavaScript
Chrome DevTools 之 Profiles,深度性能优化必备
- 使用Chrome DevTools的Performance
通过时间线Timeline,可以查看CPU端各个线程和GPU的执行顺序和热点函数的时间开销。
本系列主要用该方法测试在多线程中,每个线程的执行顺序和性能开销
相关资料可参考:
如何使用 Timeline 工具
Chrome DevTools 之 Timeline,快捷性能优化工具
- 使用Chrome DevTools的NetWork
查看各个资源的加载时间和顺序。
本系列用该方法测试在“使用函数式反应式编程的流来异步加载 js、二进制文件等资源”时,各个资源的加载顺序是否正确。
- 使用Performance.now
使用该方法打印某段逻辑的时间开销,多用于自动化测试->端对端测试—>性能测试。
本系列在使用profile定位到热点函数后,会使用该方法确定具体代码的时间开销。通过比较优化前和优化后的时间开销,来评估优化的效果。
示例代码为:
var n1 = performance.now();
执行某些逻辑
var n2 = performance.now();
//打印逻辑的时间开销(ms为单位)
console.log(n2 - n1);
- 使用Console.profile
相对于Chrome DevTools的Javascript Profiler,该方法可以测试某一段逻辑的profile,而不是整个页面的profile,粒度更小,更加可控。
(图来自Chrome 控制台console的用法(学了之后对于调试js可是大大有用的哦))
测试内存开销
- 使用Chrome DevTools的Memory
本系列对这个工具的应用:
使用Allocation sampling,定位内存占用的热点函数;
使用Allocation instrumentation on timeline来比较每一帧内存开销的增长情况,从而确定是否有内存泄漏。如果有内存泄漏,则通过Heap snapshot,记录多个内存快照并进行比较,定位到具体是哪些地方增加了内存。
相关资料可参考:
解决内存问题
搭建本地测试环境
运行测试和性能测试不仅需要使用Chrome浏览器的控制台功能,还需要:
1、安装Spector.js
Spector.js可以在Chrome的扩展中安装
2、进行移动端测试时,需要在测试页面中引入vConsole库,并使用Github Pages搭建测试服务器