前端测试框架浅试

最近在对前端做Unit Test,用到一些测试框架,这里做一些简单的记录。

主要做的事情是针对一个React-hook一些组件进行了测试,使用的是vitest,并且把原来的一些jest的测试迁移到了vitest上,并且删掉了jest的依赖。

vitest & jest

vitest比较轻量级,基于vite驱动,如果使用了vite,vitest是个很好的选择。

vitest的速度比jest快很多,但是生态比起jest略逊一筹。

vitest和jest的api都是兼容的,vitest的文档目前还比较简单,如果对某些api有疑问,可以参考jest。


  1. describe和it
1
2
3
4
5
6
7
8
import { describe, expect, it } from 'vitest';
describe('describe1', () => {
describe('describe2', () => {
it('it1', () => {
expect(1).tobe(1);
})
})
})
  1. expect

expect的api文档可以参考https://cn.vitest.dev/api/expect,这里介绍几个

  • toBe比较对象类型的时候,比较的是引用,即使结构相同,如果不是同一个实例,也会报错。
  • toStrictEqualtoEqual都是比较对象的结构,但是存在以下区别(摘自官方文档):
    • 检查具有 undefined 属性的键。 例如 使用 .toStrictEqual 时, {a: undefined, b: 2}{b: 2} 不匹配。
    • 检查数组稀疏性。 例如 使用 .toStrictEqual 时, [, 1][undefined, 1] 不匹配。
    • 检查对象类型是否相等。 例如 具有字段 ab 的类实例不等于具有字段 ab 的文字对象。
  • 但是toEqual对于undefined的检测还是较为严格(比起jest而言),因此必要时可以使用JSON.stringify,即
1
expect(JSON.stringify(A)).toEqual(JSON.stringify(B));
  • 数组长度为空
1
expect(myList).toHaveLength(0);
  1. 定义上下文context:假如说在某一系列测试中(it or test)需要相同的变量或函数,则可以将其添加在测试上下文之中,参考官方文档
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 定义上下文
interface ChartTestContext {
findChildByType: (children: Children[], types: string[]) => Children[];
}

export const chartTest: TestAPI<ChartTestContext> = it.extend<ChartTestContext>({
/**
* The 'children' structure is as follows, 'findChildByType' is utilized to search for a child with a specific type among the 'types'.
* "children": [
* {
* "some_other_keys": "some_other_values",
* "type": "certian_type"
* }
* ]
*/
findChildByType: async ({}, use) => {
use((children: Children[], types: string[]) => {
return children.filter((child) => types.includes(child.type));
});
},
});

// 测试代码
chartTest('should show line chart when type = line', ({ findChildByType }) => {
const type_line = testOptions({
source: source,
type: 'line',
});
expect(findChildByType(type_line.children, ['axisY'])[0].scale.y.domain).toEqual([-127, 119]);
expect(findChildByType(type_line.children, ['line'])).toHaveLength(1);
});
  1. 钩子函数beforeEachafterEach:当在同一个describe之中,可能需要对每个测试it都定义一个上下文,这时候就可以使用beforeEach
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 定义上下文类型
export interface LegendContext {
findLegend: (children: Children[]) => {
color: { position: string; layout: { justifyContent: string; alignItems: string } };
};
}

// 测试代码
describe('', () => {
let xxx;
renderHook(() => {
xxx = useXXXX();
})

beforeEach<LegendContext>(async (context) => {
context.findLegend = (children: Children[]) => {
return children.find((child) => child.type === 'line' || child.type === 'area')!.legend;
};
});

it<LegendContext>('', (context) => {
const legend = context.findLegend(...);
})
})

cypress

这是个端到端的前端测试框架,还没有使用过,等我用过再来填坑。