单元测试

Unit Testing 又称模块测试,是针对程序模块(软件设计最新单位,一般为一个函数)进行正确性检验的测试工作。 编写单元测试是提升代码质量和可维护性的重要手段

3A 设计原则

单元测试设计应遵守 3A(Arrange - Action - Assertion)原则,即一个测试用例需要包含安排、行动、断言三部分,并且这三部分的顺序是固定的,不能存在交叉的部分,每个部分应该有唯一的意图。

  1. Arrange 安排: 测试对象或数据的初始化
  2. Action 行动: 对初始化对象或数据进行操作
  3. Assertion 断言: 对结果进行断言

优秀的单元测试应该:

  1. 简单:将大模块拆分成小模块
  2. 可读:单一职责原则、通过 3A 原则组织代码
  3. 可靠:稳定、只有被测试代码存在问题才会失败
  4. 快速:保证每一个用例的速度
  5. 独立:无执行顺序依赖、无环境依赖,可并行执行快速启动

编写建议:

  1. 3A 原则
  2. 使用嵌套对用例进行分组排版,便于管理、理解和结果展示
  3. 保证用例的简单,不要在用例中使用判断和循环,否则很难保证用例没有 bug

TODO: TDD VS BDD

对比 TDD(Test-Driven Development) BDD(Behavior-Driven Development)
描述 测试驱动开发 行为驱动开发
思想 在编写需求功能代码前,先编写单元测试代码,再编写功能代码,满足这些之前编写的单元测试代码
面向人员 开发人员编写测试代码
开发流程 1. 编写测试用例
2. 运行测试用例,全部不通过
3. 编写代码,使测试用例通过
4. 优化代码
5. 完成开发重复以上步骤

TODO:编写可测的代码

bad 场景一:重复使用实例变量

操作对象内部变量,会产生副租用,多个方法都对对象内部变量进行操作,对象方法的执行顺序可能导致不同的结果,测试很难覆盖全部场景。 修改方案:

  1. 通过静态方法去状态化
  2. 通过函数式方法去状态化

bad 场景二:代码结果不可预测

函数内部存在不确定变量(系统时间、随机数等) 解决方案,依赖注入

bad 场景二:代码副作用

单元测试简单实现

function add(x, y) {
  return x + y;
}
function minus(x, y) {
  return x - y;
}
function multi(x, y) {
  return x + y;
}

function expect(value) {
  return {
    toBe: (actual) => {
      if (actual !== value) {
        throw new Error(
          `预期值与实际值不相等:预期值为${actual},实际值为${value}`
        );
      }
    },
  };
}
function test(desc, fun) {
  try {
    fun();
    console.log(`${desc} 通过测试`);
  } catch (e) {
    console.log(`${desc} 未通过测试(${e})`);
  }
}
test("测试加法: add(3, 7)", () => {
  expect(add(3, 7)).toBe(10);
});
test("测试减法: minus(3, 3)", () => {
  expect(minus(3, 3)).toBe(0);
});
test("测试乘法: minus(3, 3)", () => {
  expect(multi(3, 3)).toBe(9);
});

results matching ""

    No results matching ""