TypeScript项目应用Jest实践

154 阅读2分钟

公司的项目已经由javascript全面转为TypeScript,这并不代表TypeScript比JavaScript更优秀。而是随着项目规模和参与开发的团队规模的扩大,类型约束对项目的质量有非常明显的帮助。测试对于提升软件质量的帮助是被公认的,它能排除某些程序逻辑问题还是次要的,更重要的是测试还能帮助程序员理解业务乃至业务的边界。测试代码还能一定程度替代文本注释,帮助人们理解代码的意图。

开始

项目初始化,对于TS的项目,Jest支持两种方案:babelts-jest,这里选择ts-jest。安装jest以及依赖:

mkdir your-project
npm init -y
tsc init
pnpm add --save-dev jest ts-jest @types/node @types/jest ts-node

配置

Jest

npx ts-jest config:init

初始化配置文件,也可以运行

npx jest --init

❯ npx jest --init

The following questions will help Jest to create a suitable configuration for your project

✔ Would you like to use Typescript for the configuration file? … yes
✔ Choose the test environment that will be used for testing › node
✔ Do you want Jest to add coverage reports? … yes
✔ Which provider should be used to instrument code for coverage? › v8
✔ Automatically clear mock calls, instances, contexts and results before every test? … no

建议采用后者,因为后者创建的配置文件中包含全部的配置以及解释,不用到的配置也被注释掉,方便调整。

再打开配置文件,调整常见开发环境下的测试特性:

  1. 忽略测试目录:因为用TS,有些项目会先构建dist目录,然后运行,所以需要把dist目录忽略掉。testPathIgnorePatterns:["/node_modules/","/dist"]
  2. 设置转义,ts扩展名的文件,用ts-jest处理。transform:{"^.+\.(t|j)s$": "ts-jest"}

Package.json

修改tsconfig.json:输出目录outDir设置为./dist,(如有)设置.gitignore的忽略目录

实践

在src目录中创建文件foo.class.tsfoo.class.spec.ts

// foo.class.ts
export class Foo {
  constructor(private _name: string) {}

  get name(): string {
    return this._name;
  }

  set name(value: string) {
    this._name = value;
  }
}

// foo.class.spec.ts
import { Foo } from "./foo.class";

describe("Foo测试", () => {
  it("初始化", () => {
    const foo = new Foo("foo");
    expect(foo).toBeInstanceOf(Foo);
  });
  it("name属性", () => {
    const foo = new Foo("foo");
    expect(foo.name).toBe("foo");
  });
  it("设置name属性", () => {
    const foo = new Foo("foo");
    expect(() => (foo.name = "bar")).not.toThrow();
    expect(foo.name).toBe("bar");
  });
});

运行jest,应该得到如下结果:

❯ jest
 PASS  src/foo.class.spec.ts
  Foo测试
    ✓ 初始化 (3 ms)
    ✓ name属性 (1 ms)
    ✓ 设置name属性 (1 ms)

--------------|---------|----------|---------|---------|-------------------
File          | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
--------------|---------|----------|---------|---------|-------------------
All files     |     100 |      100 |     100 |     100 |                   
 foo.class.ts |     100 |      100 |     100 |     100 |                   
--------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       3 passed, 3 total
Snapshots:   0 total
Time:        3.337 s, estimated 4 s
Ran all test suites.

说明项目最基本的环境已经配置成功。

丝滑设置

以上的项目仅能保证在初始化时可以用,而实际的项目很少有如此简单的。比如说项目中会用到很多本地类库,由于路径复杂,往往会设置别名(Alias),此时必须同步tsconfig和jest.config两者的配置。例如tsconfig.json有:

"baseUrl": "./" ,
"paths": {
      "src/*": ["./src/*"],
      "common": ["./src/common"]
    }

src/common/index.ts创建简单的内容

export const DEFAULT_NAME="Tester"

修改src/foo.class.ts

import { DEFAULT_NAME } from "common";
export class Foo {
  constructor(private _name: string = DEFAULT_NAME) {}

  get name(): string {
    return this._name;
  }

  set name(value: string) {
    this._name = value;
  }
}

此时,如果不做任何操作,程序可以被正确构建,但是无法通过jest,因为此时的jest,还无法正确识别common。所以需要在jest.config.ts中加入:

  // …… 其他内容
  moduleNameMapper: {
    "^common$": "<rootDir>/src/common",
  },
  // ……其他内容

效率

(如果电脑的性能还OK)实时监控程序改变(保存),触发重新运行测试代码。

jest --watchAll

vscode

vscode的插件,打开的项目中如果有jest的配置,会自动执行测试脚本。

ext install Orta.vscode-jest