单元测试又来了。不过这次是TS + 老朋友Jest
TS使用Jest进行单测的方案,之前研究过,但很麻烦,本质上还是JS,使用babel将项目和测试的TS代码进行一次转译,然后就可以和普通的JS代码一样单测了。
后来!我发现了ts-jest!可以直接执行TS代码,当然肯定还是转译了,但对于使用者来说,却方便了不少,接入方案也非常简单!
本文测试环境为2.4,理论上3.x也可。
如果你的项目是JS,可以参考我之前的文章:
进入正文吧~
一、接入Jest
1. 安装
简单快速,npm上手就来:
npm install --save-dev ts-jest
npm install --save-dev @types/jest
第二个库是类型提示,不要漏了~
2. 配置
在根目录下新建jest.config.js文件:
// jest.config.js
/** @type {import('ts-jest').JestConfigWithTsJest} */
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
roots: ['<rootDir>/__tests__/assets'],
setupFiles: []
};
在根目录下创建__tests__文件夹及assets子文件夹。建议可以从assets开始使用和项目相同的目录结构,与被测试文件一一对应。
当然也可以是别的名字/目录,记得同步修改jest.config.js中的roots。
修改package.json,增加test脚本:
// package.json
{
"scripts": {"test": "jest"}
}
这样我们可以通过cmd执行 npm run test
来运行所有测试脚本。
3. 测试
是的,已经能把单测跑起来了…
我们在项目内写一段测试代码(你可以直接用项目里已有的代码):
// MyUtils.ts
export class MyUtils {
public static fastRemoveArrayItemAt<T>(array: T[], index: number) {
if (array) {
let length = array.length;
if (index < 0 || index >= length) {
return false;
}
array[index] = array[length - 1];
array.length = length - 1;
}
return true;
}
}
在__tests__/assets目录下新建test.spec.ts文件:
import { MyUtils } from "../assets/Script/MyUtils";
describe("fastRemoveArrayItemAt", () => {
test("数组越界", () => {
expect(MyUtils.fastRemoveArrayItemAt([1, 2, 3], 0)).toBe(true);
expect(MyUtils.fastRemoveArrayItemAt([1, 2, 3], 4)).toBe(false);
expect(MyUtils.fastRemoveArrayItemAt([1, 2, 3], -1)).toBe(false);
});
});
cmd执行测试代码:
npm run test
运行结果:
看到绿色的PASS就可以啦~
二、兼容Cocos
代码中不免会使用Cocos提供的类/函数,比如cc.Vec2等,兼容Cocos是必要的,不然有很多代码一跑就报错。
1. 环境搭建
在__tests__文件夹下新建env子文件夹。
找出引擎下的cocos2d-js-for-preview.js,复制到env文件夹下。
安装支持库:
npm install --save-dev jest-canvas-mock
npm install --save-dev jest-environment-jsdom
修改jest.config.js:
/** @type {import('ts-jest').JestConfigWithTsJest} */
module.exports = {
preset: 'ts-jest',
testEnvironment: 'jsdom',
roots: ['<rootDir>/__tests__/assets'],
setupFiles: [
'jest-canvas-mock',
'<rootDir>/__tests__/env/cocos2d-js-for-preview.js'
]
};
注意setupFiles的顺序,不能反过来!
2. 测试
MyUtils.ts增加代码:
export class MyUtils {
public static getX(vec: cc.Vec2): number {
return vec.x;
}
}
test.spec.ts新增测试用例:
describe("测试cocos", () => {
test("随便写的", () => {
let vec = cc.v2(100, 100);
expect(MyUtils.getX(vec)).toBe(100);
});
});
一时没想到什么测试用例,随便写了一个…
还是npm run test:
这步只要能跑起来就行了,如果cocos没有正确加载就会抛出cc undefined之类的报错。
3. 小结
Jest的接入其实差不多也就这样了。TS由于需要编译,反而比JS省去了支持require等工作。
接下来就可以愉快地使用了~
不过我也顺便研究了一下,有没有可能,支持一下界面调试?
三、兼容Cocos-升级版
丑话放前头… 一般来说,逻辑测过了,界面不会有什么问题,只是纯好奇。
1. 环境搭建
把Cocos内置的预览界面复制到env文件夹下:
在env文件夹下新建setup-cocos.ts文件:
const fs = require("fs");
const path = require("path");
const html = fs.readFileSync(path.resolve(__dirname, "index.html"), "utf8");
document.documentElement.innerHTML = html;
var canvas = document.getElementById("GameCanvas");
var option = {
id: canvas,
debugMode: 1,
showFPS: false,
frameRate: 60,
groupList: ["default"],
collisionMatrix: [[true]],
};
cc.game.run(option, () => {});
修改根目录下的jest.config.js,增加setup-cocos.ts到setupFiles:
/** @type {import('ts-jest').JestConfigWithTsJest} */
module.exports = {
preset: 'ts-jest',
testEnvironment: 'jsdom',
roots: ['<rootDir>/__tests__/assets'],
setupFiles: [
'jest-canvas-mock',
'<rootDir>/__tests__/env/cocos2d-js-for-preview.js',
'<rootDir>/__tests__/env/setup-cocos.ts'
]
};
注意顺序,要在cocos2d-js-for-preview.js后面!
2. 测试
项目下新建FixContentLabel.ts:
const { ccclass, property } = cc._decorator;
@ccclass
export default class FixContentLabel extends cc.Label {
protected onLoad(): void {
this.string = "固定文本";
}
}
在test.spec.ts中新增测试用例:
describe("FixContentLabel", () => {
test("文本检查", () => {
let scene = new cc.Scene();
cc.director.runSceneImmediate(scene);
let node = new cc.Node();
scene.addChild(node);
node.active = true;
let label = node.addComponent(FixContentLabel);
expect(label.string).toEqual("固定文本");
});
});
执行,得到以下结果:
可以愉快地测试组件了!
四、辅助工具
再次推荐Vscode Jest插件!四个字,非常好用!
在插件市场搜索Jest即可,长这样:
提供了测试面板,可以选择性测试某个文件夹、某个文件、甚至单独执行某个函数:
每个会在函数的前面生成一个图标,可以快速执行,或者debug执行:
(那个绿色勾勾就是)
个人广告
公众号文章链接: 使用Jest实现CocosCreator+TS项目的单元测试
专注于游戏开发相关的技术与信息分享的公众号
感兴趣的朋友可以关注一波~