测试Node.js应用程序
Node.js被用来开发从简单的投资组合网站到复杂的API和数百万人使用的应用程序。随着应用程序规模的增长,出现bug的风险也在增长。一个应用程序在经过测试之前是不完整的。测试可以是一个简单的console.log 到一个函数,看看它是否按预期工作。
后台代码承载着我们应用程序的所有业务逻辑,我们必须把它做好。部署有错误的代码会使一个组织损失数百万美元。由于Node.js正在被用于世界各地的关键应用程序,我们必须测试我们的应用程序。手动测试很繁琐,而且容易出现人为错误;自动测试包括编写逻辑来测试你的代码,而不是通过手动检查每个函数或类来测试你的应用程序的功能。
测试一个应用程序有几个阶段,最普遍接受的阶段是。
- 单元测试。编写单元测试是为了测试代码的特定部分的功能,如测试一个类或一个函数,下面详细讨论。
- 集成测试。在集成测试中,软件单元被组合在一起并进行测试,它被用来检测接口和集成组件之间的缺陷。意思是说,如果你在Node.js中导出(module.exports)和导入(required)代码,可以进行集成测试,看它们是否都按预期工作。这种类型的测试的问题是,它要处理大量的代码,可能很难找出错误的原因。它通常是在单元测试之后进行。
- 系统测试:系统测试是在一个完全集成的系统上进行的,以验证它是否满足所需的需求。
- 验收测试:通常情况下,是测试的最后一级,在向消费者发布之前,对产品的准备情况进行测试。
下面我们来看看单元测试作为自动化测试的一种方法。
function addNumbers(a,b){
return a+b;
}
console.log(addNumbers(1,2));
单元测试
单元测试是自动化测试的一种类型,你写逻辑来测试你的应用程序的离散部分。单元测试直接在函数或方法层面上测试代码逻辑,它适用于所有类型的应用程序。编写测试使你思考你的应用设计选择,并帮助你及早避免陷阱。单元测试方法可以分为两种主要形式:测试驱动的开发和行为驱动的开发。

单元测试
用Node的单元测试assert 模块。
大多数Node测试的基础是内置的assert模块,它测试一个条件,如果不满足这个条件,就抛出一个错误。
让我们看一下断言测试的一个简单例子。
//index.js
function addTwo(a){
return a+2; //a normal function
}
function testAddTwo() {
var x = 5;
var x2 = x + 2;
var x3 = addTwo(x);
console.log("Expected "+x2+" Got " + x3);
if (x2 === x3){
return console.log("Passed");
}
console.log("Failed");
}
testAddTwo();
在上面的例子中,我们使用了一个x=5 的测试案例,当我们运行这个脚本时,我们可以在控制台看到。
Expected 7 Got 7
Passed
结构化单元测试
让我们研究一下结构化测试的一种方式,AAA模式。一个测试有3个部分,安排,行动,断言。
- 安排。初始化变量,设置必要的前提条件和输入。
- 行动。执行被测试的函数/单元。
- 断言。断言(或检查)从执行单元得到的值与预期的值相同。
上面的代码是
//index.js
function addTwo(a){
return a+2;
}
function testAddTwo() {
//Step 1. Arrange
var x = 5;
var x2 = x + 2;
//Step 2. Act
var x3 = addTwo(x);
console.log("Expected "+x2+" Got " + x3);
//Step 3. Assert
if (x2 === x3){
return console.log("Passed");
}
console.log("Failed");
}
testAddTwo();
上面代码例子中的if 条件*,断言*代码是否正确工作。Node.js自带了一个assert模块。让我们用assert 来做同样的动作。
var assert = require("assert")
function addTwo(a){
return a+2;
}
function testAddTwo(){
var x = 5;
var y1 = x + 2;
var y2 = addTwo(x);
console.log("Expected "+y1+" Got " +y2);
try{
assert.equal(y1,y2);
console.log("Passed");
} catch (err) {
console.log("Failed");
console.log(err);
}
}
testAddTwo();
对于正确的代码,我们可以在控制台期待以下结果。
Expected 7 Got 7
Passed
当我们故意将var y1 = x + 2; 改为var y1 = x + 1; ,我们会看到以下错误信息。
Expected 6 Got 7
Failed
AssertionError [ERR_ASSERTION]: 6 == 7
{
generatedMessage: true,
code: 'ERR_ASSERTION',
actual: 6,
expected: 7,
operator: '=='
}
equal 测试一个变量的内容是否确实等于第二个参数中指定的值。让我们看看assert模块中的各种方法以及我们如何使用它们。
例子。
assert.equal(a,b,c).如果a不等于b,抛出带有可选的信息c的断言错误,如果没有提供c,则显示默认信息。assert.notEqual(a, b,c).如果a等于b,抛出带有可选信息c的错误。当应用程序生成的某个值表明逻辑上有问题时使用。assert.strictEqual(a,b,c).这使用严格的平等(===)而不是(==)。如果a和b不严格相等,则抛出错误,并带有可选的信息c。assert.deepEqual(a,b,c).这是对对象的比较。它们递归地比较两个对象,比较两个对象的属性,如果属性本身是对象,也比较这些对象。assert.ok(val, mes).这可用于测试异步函数。当我们把它作为assert.ok(value, error_message),如果值是假的,那么它就会抛出error_message。
例子。
function doAsync (callback) {
setTimeout(callback, 2000, true); // "callback" function with arguements "true"
}
function testAsync (callback) {
doAsync(function (value) {
assert.ok(value, "Callback should be passed true, got false");
callback(); //trigger callback when done
}) //if we deliberately change the value in doAsync to false then we can see the error.
}
Mocha
Mocha是一个流行的JavaScript测试框架,在Node.js和浏览器上运行。Mocha很简单,可扩展,速度快,它用于单元和集成测试。Mocha没有内置的断言,所以它与assert、chai等库一起使用。Mocha提供方便的异步和同步测试,界面简单。
安装和设置
创建一个新的目录并初始化一个node项目(如果你还没有node项目)。
mkdir test-project
cd test-project
npm init -y
npm install --save-dev mocha chai
添加test 脚本到你的package.json 。
//package.json
...
{
"scripts": {
"test": "mocha"
}
}
...
用Mocha和Assert进行单元测试
Mocha遵循这个模板来编写测试。
describe([String with Test Group Name], function() {
it([String with Test Name], function() {
[Test Code]
});
});
describe() 函数用于分组类似的测试,分组测试使我们的测试代码更容易维护。it() 包含我们的测试代码。我们将使用Mocha的BDD接口。让我们写一个简单的函数,在数字上添加 "2"(与上面的例子相同)。
//addTwo.js
// adds two to a number, if an array is passed adds 2 to every number, if non-number then throws error.
module.exports = function() {
var args = Array.prototype.slice.call(arguments);
if (!args.every(Number.isFinite)) {
throw new TypeError('addTwo() exprects only numbers or array of numbers')
}
return args.map(x => x+2);
}
创建一个名为test 的子目录,并在该目录内创建一个名为testAddTwo.js 的文件(无所谓)。我们在这个例子中为不同的情况添加测试。
var addt = require("../addTwo.js");
var assert = require("assert");
var expect = require("chai").expect;
describe("addTwo()", function() {
context("Not passing any arguments", function(){
it("should return 0", function(){
assert.equal(addt(), 0);
})
})
context("Passing proper number", function(){
it("should add 2", function(){
assert.equal(addt(1), 3);
})
})
context("With an array of numbers", function(){
it("should add 2", function(){
assert.deepEqual(addt([1,2,3]) , [3,4,5]); #comparing objects so "deepEqual"
})
})
context("With non-numbers", function(){
it("should throw error", function(){
expect(function(){
addt([1,"as", 1])
}).to.throw(TypeError, 'addTwo() exprects only numbers or array of numbers')
})
})
})
我们在最后一个测试中使用了chai断言库,在这里我们可以看到断言模块可能只是没有覆盖的地方。Chai提供了三种断言样式assert,expect 和should - 你可以在这里阅读它们的区别和用途。
当我们运行命令npm test ,我们可以看到以下结果。

当我做了一些改变以导致错误时,这是输出结果。

用Mocha测试异步代码
大多数Node.js应用程序使用大量的异步代码。Mocha也可以用非常相似的语法来测试异步代码。下面是一个使用async await 和callbacks 的异步函数的例子,取自mochajs.org。
//callbacks
describe('User', function () {
describe('#save()', function () {
it('should save without error', function (done) {
var user = new User('Luna');
user.save(function (err) {
if (err) done(err);
else done();
});
});
});
});
//async
beforeEach(async function () {
await db.clear();
await db.save([tobi, loki, jane]);
});
describe('#find()', function () {
it('responds with matching records', async function () {
const users = await db.find({type: 'User'});
users.should.have.length(3);
});
});
我们将done 作为it() 的参数,done() 的回调函数被Mocha用来告诉它异步函数何时完成。
总结
在编写测试时要记住,测试是为了让你的生活更轻松,把你的测试设计得简单、简短和易懂。点击这里查看最佳实践。