在开始聊测试规范之前,如果你还在写测试这件事情上苦苦挣扎,可以瞅瞅我的 另一个仓库 我会在这个仓库中慢慢整理我在项目中遇到的,大家都很苦恼的一个测试场景,目的是为了让大家有作业可以抄。
这里的测试规范我想先聚焦于单元测试,如果后面机会再扩展到诸如 UI、E2E 之类的更高层级的测试规范。而技术栈我会默认是常规的 Jest + React Testing Library + React Testing Library/React Hooks。但是理论上这些规范都可以扩展到不同的技术栈当中去,重要的是思想。
首先也是我们之前有提到过的,测试文件应该位于被测文件同级目录下的 __tests__
目录下,以便于快速定位两个相联文件的位置。
在测试时你的所有测试应该被一个最外层的 describe
表达式包裹,这个 describe
的第一个字符串参数应该是 '#' 加你将要测试的文件名。用以告知读代码的人这是这个测试的最外层。
如果你的文件中有多个独立的方法需要测试(这种情况一般出现在 utils/helpers 文件中)那么你应该使用第二层 describe
将每一个待测方法的测试用例包裹起来,这时当前这个 describe
的字符串参数应该是 '##' 加你将要测试的方法名,下面是一个没什么意义的简单的例子。
describe("# utils", () => {
describe("## isSomeFiledsRequired", () => {
it("should return false when input is false", () => {
expect(isSomeFiledsRequired(false)).toBeFalsy();
},
);
it("should return true when input is true", () => {
expect(isSomeFiledsRequired(true)).toBeTruthy();
},
);
});
describe("## isSomeFiledsOptional", () => {
it("should return false when input is false", () => {
expect(isSomeFiledsOptional(false)).toBeFalsy();
},
);
it("should return true when input is true", () => {
expect(isSomeFiledsOptional(true)).toBeTruthy();
},
);
});
});
这样分级的意义在于如果你的文件中有多个方法时,你的测试将更加可读,能够一眼看出你的每一个方法的测试在哪里,便于修改和管理。
每一个测试用例应该拥有有可读性强的用例描述,同时为了统一用例描述,我们推荐你采用如下的用例描述格式:
should xxxxx given xxxxxx when xxxxxx
根据不同的场景 given
的部分可以省略,这样的句式能够让看测试的人快速明白这个测试用例的目的是什么,条件是什么。可以结合用例的代码快速理解整个测试用例的目的。
每一个测试用例在编写的时候不应该包含任何逻辑,只应该调用目标函数/组件,然后对它们的输出进行断言。
其次每一个测试用例都应该遵循 given when then 的原则,并且在某一个步骤超过三行的情况下添加注释
- 准备好被测函数/组件需要的输入/mock
- 调用目标函数/组件
- 对它们的输出进行断言
it("should return xxx given yyy is zzz when is some business case", () => {
// given
const param1 = { a: 1, b: 2 };
const param2 = jest.fn();
const param3 = jest.fn();
const param4 = "dididi";
// when
const result = Foo({ param1, param2, param3, param4 });
// then
expect(result).toBe("lalala");
expect(param2).toBeCalledWith("biubiubiu");
expect(param3).toBeCalledTimes(2);
});
如果你的多个测试仅仅是为了测试某个方法/组件在不同参数下的不同输出,请使用 参数化测试 来简化你的测试。
这不但可以让你的测试用例更加简洁,也能够让代码阅读者更容易理解你的测试。