测试Node.js应用程序的方法

91 阅读7分钟

测试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));

单元测试

单元测试是自动化测试的一种类型,你写逻辑来测试你的应用程序的离散部分。单元测试直接在函数或方法层面上测试代码逻辑,它适用于所有类型的应用程序。编写测试使你思考你的应用设计选择,并帮助你及早避免陷阱。单元测试方法可以分为两种主要形式:测试驱动的开发行为驱动的开发

img

单元测试

用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个部分,安排,行动,断言。

  1. 安排。初始化变量,设置必要的前提条件和输入。
  2. 行动。执行被测试的函数/单元。
  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).这使用严格的平等(===)而不是(==)。如果ab不严格相等,则抛出错误,并带有可选的信息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没有内置的断言,所以它与assertchai等库一起使用。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,expectshould - 你可以在这里阅读它们的区别和用途。

当我们运行命令npm test ,我们可以看到以下结果。

img

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

img

用Mocha测试异步代码

大多数Node.js应用程序使用大量的异步代码。Mocha也可以用非常相似的语法来测试异步代码。下面是一个使用async awaitcallbacks 的异步函数的例子,取自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用来告诉它异步函数何时完成。

总结

在编写测试时要记住,测试是为了让你的生活更轻松,把你的测试设计得简单、简短和易懂。点击这里查看最佳实践。