Mocha+Chai+Sinon 学习笔记 (四)—— 测试异步代码的正确姿势
在前端开发中,异步代码的使用十分常见,例如 Ajax 的请求、setTimeout 和 setInterval 的定时器、Promise 的异步操作等,这些异步操作给前端测试带来了一定难度。本篇文章将为大家介绍如何使用 Mocha、Chai 和 Sinon 库来测试异步代码,帮助大家掌握测试异步代码的正确姿势。
前置知识
在开始学习测试异步代码之前,我们需要先了解一些前置知识。
异步代码
异步代码是指不会立即返回结果的代码,而是在未来某个时间返回结果。例如,Ajax 请求就是异步代码,因为它需要等待服务器响应才能返回数据。异步代码执行时,程序无法继续向下执行,而是需要等待异步代码执行完毕后才能继续执行下一步操作。
测试框架
测试框架是指用于方便地编写、运行和管理测试用例的工具。 Mocha 是一款流行的 JavaScript 测试框架,它支持测试异步代码、浏览器和 Node.js 等多种环境。
断言库
断言库是指用于编写测试用例中的断言(即期望值与实际值的对比)的工具。 Chai 是一个流行的 JavaScript 断言库,它提供了多种 API 来检查变量的值,例如断言是否相等、是否为假值等。
模拟库
模拟库是指用于模拟函数、对象等行为的工具。 Sinon 是一个流行的 JavaScript 模拟库,它支持模拟函数的返回值、参数和函数执行情况等。
测试异步代码的正确姿势
在我们开始测试异步代码之前,需要清楚地明白一个问题:JavaScript 是单线程的,即任何时刻只能有一条指令被执行。因此,当 JavaScript 执行异步代码时,它不能像同步代码一样等待异步代码执行完毕再返回结果,而需要使用回调函数、Promise、async/await 等方式来处理异步操作。
在测试异步代码时,我们需要等待异步代码执行完毕后再进行断言。由于异步代码的执行时间是无法确定的,因此我们需要使用测试框架提供的一些特殊功能来处理异步操作。
Mocha 的异步支持
Mocha 提供了多种方式来支持测试异步代码,例如:
done()
done() 函数是 Mocha 提供的一种用于测试异步代码的方式。当一个测试用例使用 done() 函数时,Mocha 将等待 done() 函数被调用后再结束测试用例。例如:
it('should get user data', function(done) {
getUserData(function(data) {
expect(data.name).to.equal('John');
done();
});
});在上述例子中,我们使用 getUserData 函数异步获取用户数据,并在回调函数中进行断言,然后调用 done() 函数告诉 Mocha 这个测试用例已经完成。
Promise
Mocha 支持测试 Promise 的异步代码,只需将返回 Promise 的函数返回值传递给 it() 函数即可。例如:
it('should get user data', function() {
return getUserData().then(function(data) {
expect(data.name).to.equal('John');
});
});在上述例子中,我们使用 getUserData 函数异步获取用户数据,并在 then() 中进行断言,然后将 Promise 返回值传递给 it() 函数告诉 Mocha 这个测试用例已经完成。
async/await
Mocha 还支持测试 async/await 的异步代码,只需在测试函数前加上 async 关键字并使用 await 关键字即可。例如:
it('should get user data', async function() {
const data = await getUserData();
expect(data.name).to.equal('John');
});在上述例子中,我们使用 getUserData 函数异步获取用户数据,并在 await 后进行断言,然后使用 async 关键字告诉 Mocha 这个测试用例是异步的。
Sinon 的异步支持
Sinon 提供了多种方式来支持测试异步代码,例如:
clock
clock 是 Sinon 提供的用于测试定时器的工具。当我们需要测试定时器相关的异步代码时,可以使用 clock 来模拟时间流逝。例如:
it('should call callback after 1 second', function() {
const callback = sinon.spy();
setTimeout(callback, 1000);
sinon.clock.tick(1000);
expect(callback.calledOnce).to.be.true;
});在上述例子中,我们使用 setTimeout 异步调用 callback 函数,并使用 sinon.clock.tick() 函数将时间向前推进 1 秒钟。然后断言 callback 函数是否被调用一次。
stub
stub 是 Sinon 提供的用于模拟函数的工具。当我们需要测试异步代码中函数的行为时,可以使用 stub 来模拟函数的返回值、参数和执行情况等。例如:
it('should call getUserData with correct arguments', async function() {
const getData = sinon.stub(api, 'getData');
getData.resolves({ name: 'John' });
await getUserData();
expect(getData.calledOnceWith('test')).to.be.true;
});在上述例子中,我们使用 sinon.stub() 函数模拟 api.getData() 函数,并设置其返回值为 { name: 'John' }。然后测试 getUserData 函数是否正确地调用了 api.getData() 函数,并传递了正确的参数。
示例代码
下面是一个完整的测试异步代码的示例代码。该示例中,我们模拟了一个异步请求,使用 Mocha、Chai 和 Sinon 库来测试异步请求是否正确地返回了指定数据。
// www.javascriptcn.com code example
// 异步请求函数
function ajax(url, callback) {
const xhr = new XMLHttpRequest();
xhr.onload = function() {
if (xhr.status === 200) {
callback(JSON.parse(xhr.responseText));
}
};
xhr.open('GET', url);
xhr.send();
}
// 测试异步请求函数
describe('ajax()', function() {
it('should return correct data', function(done) {
const data = { name: 'John', age: 25 };
const server = sinon.fakeServer.create();
server.respondWith('GET', '/user', [
200, { 'Content-Type': 'application/json' },
JSON.stringify(data)
]);
ajax('/user', function(response) {
expect(response).to.deep.equal(data);
server.restore();
done();
});
server.respond();
});
});在上述示例代码中,我们使用 sinon.fakeServer.create() 函数模拟了一个 HTTP 服务器,并使用 server.respondWith() 函数设置服务器响应内容为指定数据。然后使用 ajax() 函数发起异步请求,并在回调函数中进行断言。最后使用 server.restore() 函数来还原服务器状态。当 done() 函数被调用时,测试用例结束。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/67d6b3a7a941bf7134c8a255