【开发技巧】使用Jest实现CocosCreator+TS项目的单元测试

单元测试又来了。不过这次是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

运行结果:

image

看到绿色的PASS就可以啦~

二、兼容Cocos

代码中不免会使用Cocos提供的类/函数,比如cc.Vec2等,兼容Cocos是必要的,不然有很多代码一跑就报错:cold_face:

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:

image

这步只要能跑起来就行了,如果cocos没有正确加载就会抛出cc undefined之类的报错。

3. 小结

Jest的接入其实差不多也就这样了。TS由于需要编译,反而比JS省去了支持require等工作。

接下来就可以愉快地使用了~

不过我也顺便研究了一下,有没有可能,支持一下界面调试?:sunglasses:

三、兼容Cocos-升级版

丑话放前头… 一般来说,逻辑测过了,界面不会有什么问题,只是纯好奇:rofl:

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("固定文本");
    });
});

执行,得到以下结果:

image

:nerd_face:可以愉快地测试组件了!

四、辅助工具

再次推荐Vscode Jest插件!四个字,非常好用!

在插件市场搜索Jest即可,长这样:

image

提供了测试面板,可以选择性测试某个文件夹、某个文件、甚至单独执行某个函数:

image

每个会在函数的前面生成一个图标,可以快速执行,或者debug执行:

image

(那个绿色勾勾就是)


个人广告

公众号文章链接: 使用Jest实现CocosCreator+TS项目的单元测试

专注于游戏开发相关的技术与信息分享的公众号
感兴趣的朋友可以关注一波~

5赞

:+1::+1::+1::+1::+1::+1:

好东西 :+1: