在前端开发中,我们经常需要编写和维护各种前端组件,例如弹窗、轮播图、下拉框等。为了保证代码质量和功能正确性,我们需要对这些组件进行单元测试。本文将介绍如何使用 Chai 及 Sinon.js 对 jQuery 写的前端组件进行单元测试。
Chai
Chai 是一个断言库,它可以让我们写出更加具有表现力的测试代码。 Chai 提供了三种不同的断言风格,分别是 Assert、Expect 和 Should。
Assert 风格
Assert 风格是最基本的 断言 风格,通过 assert 对象暴露的各种方法来实现断言。比如:
assert.equal(3, 3, '这里应该相等');
assert.strictEqual(true, true, '这里应该恒等');
assert.notEqual(1, 2, '不等');
assert.notStrictEqual(2, '2', '不恒等');
assert.deepEqual({ a: 1, b: 2 }, { a: 1, b: 2 });Expect 风格
Expect 风格有点类似于 Assert 风格,在使用时需要先对一个期望值调用 expect 方法,然后在期望值上添加断言。比如:
expect(3).to.equal(3);
expect(true).to.be.true;
expect([1, 2]).to.include(1);
expect({ a: 1, b: 2 }).to.have.all.keys('a', 'b');Should 风格
Should 风格不同于前两种风格,该风格断言语句的主语是被进行断言的对象(例如一个变量)。这样看上去代码更具有可读性。比如:
(3).should.be.a('number');
true.should.be.true;
[1, 2].should.include(1);
({ a: 1, b: 2 }).should.have.all.keys('a', 'b');从上面的例子中可以看到,Chai 提供了丰富的断言方法,使得我们可以对各种类型的值进行单元测试。
Sinon.js
Sinon.js 是一个用于测试 JavaScript 代码的库,它提供了各种工具函数和 API,例如支持创建测试用的可已触发回调函数的虚拟对象和间谍的库、是网络请求失败立即返回的测试用虚拟服务器、捕捉函数中所有的函数调用,等等。它为开发人员提供了一种将可预测的行为注入到基于框架的 JavaScript 应用中的方式。
Spies
Sinon.js 的 Spy 适用于测试指定行为、函数的调用情况,以及测试是否符合预期。Spy 支持 stub 所有的方法、属性、访问器和原型方法,以提供更多的控制。比如:
// 测试 console.log 是否被调用
const spy = sinon.spy(console, 'log');
printMessage('hello world');
expect(spy.calledWith('hello world')).to.be.true;
spy.restore();Stubs
Sinon.js 的 Stub 适用于在测试中协调依赖。Stubs 提供了机制使异常或异步行为简化从而使测试更可控和可靠。比如:
// 测试原函数被调用了而又不太在意其返回值
const stub = sinon.stub($, 'ajax');
updateUser().then(() => {
expect(stub.calledOnce).to.be.true;
stub.restore();
});Mocks
Sinon.js 的 Mock 是对具体行为的一个预期,它们通常与 stub 并行使用,以防止意外的行为。Mock 会投票对特定行为的期望结果,以便在失败时引发 AssertionError。比如:
// 测试 Mock 中的特定函数是否被调用
const mock = sinon.mock(myObj);
mock.expects('method').once();
myObj.method();
mock.verify();
mock.restore();单元测试示例
考虑一个带有开关的组件,它具有以下功能:
- 初始状态下,组件处于关闭状态。
- 单击开关,组件会切换状态:从关闭到开启,从开启到关闭。
接下来我们要编写一个测试文件,对该组件进行单元测试。
// www.javascriptcn.com code example
describe('toggle component', () => {
let container;
let toggle;
beforeEach(() => {
container = $('<div class="toggle-container">');
toggle = new Toggle(container);
$('body').append(container);
});
afterEach(() => {
container.remove();
});
describe('toggle on click', () => {
it('should toggle off when clicked once', () => {
toggle.onClick();
expect(toggle.checked).to.be.true;
toggle.onClick();
expect(toggle.checked).to.be.false;
});
});
});我们使用了 beforeEach 作为在每个测试用例之前的准备工作。然后我们测试了一旦在组件上单击开关,它将触发 Toggle 类中的 onClick 方法,并检查开关的状态切换是否按预期进行。
具体的实现可以参考以下示例代码。
// www.javascriptcn.com code example
class Toggle {
constructor(container) {
this.checked = false;
this.$container = container;
this.$toggle = $('<div class="toggle">')
.click(this.onClick.bind(this))
.appendTo(container);
}
onClick() {
this.checked = !this.checked;
this.$toggle.toggleClass('active');
}
}指导意义
本文介绍了如何使用 Chai 及 Sinon.js 对 jQuery 写的前端组件进行单元测试,详细讲述了 Chai 提供的三种不同的断言风格,以及 Sinon.js 中的 Spies、Stubs、Mocks。同时,本文还结合示例代码,展示如何对一个简单的前端组件进行单元测试。
单元测试在业务逻辑、代码重构等方面都具有很大的指导意义,多数公司都会进行单测覆盖率的要求,因此在实际开发中我们应当重视单元测试,从而提高代码的质量。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/67cfa1a6e46428fe9eb51c9b