单元测试与单元测试框架 Jest

今天节目的主要内容

  • 什么是单元测试?
  • Jest 简介
  • 实际案例分析

测试是一种验证我们的代码是否可以按预期工作的手段。

单元测试特指被测试对象为程序中最小组成单元的测试。

开发简单

执行速度快

有助于促进我们更好地设计代码

单元测试的优势

单元测试的限制/不足

再简单的代码也需要开发,需要平衡投入与收益

容易给开发人员带来错觉

什么时候编写单元测试?

开发代码的时候

维护代码的时候

单元测试中的基本概念

  • 被测试的对象是什么
  • 要测试该对象的什么功能
  • 实际得到的结果
  • 期望的结果
  • mock / spy (下文会详述)

单元测试的基本步骤

  • 准备阶段:构造参数,创建 spy 等
  • 执行阶段:用构造好的参数执行被测试代码
  • 断言阶段:用实际得到的结果与期望的结果比较,以判断该测试是否正常
  • 清理阶段:清理准备阶段对外部环境的影响,移除在准备阶段创建的 spy 等

Jest

Jest 是 Facebook 开发的一款 JavaScript 测试框架。在 Facebook 内部广泛用来测试各种 JavaScript 代码。

  • 轻松上手
  • 内置强大的断言与 mock 功能
  • 内置测试覆盖率统计功能
  • 内置 Snapshot 机制
describe('Addition', () => {
  it('knows that 2 and 2 make 4', () => {
    const val1 = 2;
    const val2 = 2;
    const result = val1 + val2;
    const expectedResult = 4;
    expect(result).toBe(expectedResult);
  });
});

Jest 中的 mock 与 spy

function forEach(items, callback) {
  for (let index = 0; index < items.length; index++) {
    callback(items[index]);
  }
}
describe('forEach', () => {
  it('should call callback with each item', () => {
    const callHistory = [];
    const specialCallback = (...args) => callHistory.push(args);
    forEach([1, 2], specialCallback);

    expect(callHistory.length).toBe(2);
    expect(callHistory[0][0]).toBe(1);
    expect(callHistory[1][0]).toBe(2);
  })
});
describe('forEach', () => {
  it('should call callback with each item', () => {
    const mockFn = jest.fn();
    forEach([1, 2], mockFn);

    expect(mockFn.mock.calls.length).toBe(2);
    expect(mockFn.mock.calls[0][0]).toBe(1);
    expect(mockFn.mock.calls[1][0]).toBe(2);
  })
});

spy

const bot = {
  sayHello: (name) => {
    console.log(`Hello ${name}!`);
  }
}
describe('bot', () => {
  it('should say hello', () => {
    const spy = jest.spyOn(bot, 'sayHello');

    bot.sayHello('Michael');

    expect(spy).toHaveBeenCalledWith('Michael');

    spy.mockRestore();
  })
});

案例分析

const domains = [
  'img10.360buyimg.com',
  'img11.360buyimg.com',
  'img12.360buyimg.com',
  'img13.360buyimg.com',
  'img14.360buyimg.com',
];

const getImageDomain = (skuId) => {
  if (skuId) {
    return domains[skuId % 5];
  } else {
    return domains[Math.floor(Math.random() * 5)];
  }
}
describe('getImageDomain', () => {
  it('should select domain based on skuId if provided', () => {
    expect(getImageDomain(1)).toBe('img11.360buyimg.com');
  });

  it('should select a random domain based on Math.random if skuId not available', () => {
    const spy = jest.spyOn(Math, 'random').mockImplementation(() => 0.9);

    expect(getImageDomain()).toBe('img14.360buyimg.com');
    expect(spy).toHaveBeenCalled();

    spy.mockRestore();
  });
});

单元测试与单元测试框架 Jest

By loveky

单元测试与单元测试框架 Jest

  • 1,159