使用 Mocha 和 Chai 测试 Node.js 代码

介绍

编写单元测试是初学者和经验丰富的工程师通常会推迟到后期开发阶段的事情,然而,它们是稳定和健壮的软件开发的关键。

基本前提 测试驱动开发 (TDD) 甚至在你开始编码之前就开始编写你的测试。 这是一个值得为之奋斗的伟大目标,但是当您试图遵循它的原则时,它需要大量的纪律和计划! 为了简化整个过程,您可以求助于易于使用且功能强大的测试和断言框架,例如 摩卡湾仔.

在本文中,我们将首先向您介绍这两个库,然后向您展示如何结合使用它们来快速创建可读且功能强大的单元测试。

湾仔

Chai 是一个断言库,它同时提供 BDD(行为驱动开发)TDD(测试驱动开发) 用于测试代码的编程风格,旨在与允许您组织测试的测试库配对。 它通常与摩卡搭配。

它具有三个主要的 API:

  • should
  • expect
  • assert

var.should.equal(var2)


expect.var.to.be.equal(var2)


assert.equal(var1, var2)

在整篇文章中,我们将重点关注使用 Chai 的 BDD 风格 expect 界面,尽管根据您自己的直觉使用其他界面/样式是完全可以的。 这 assert interface 是最相似的常见 TDD 断言框架。

expect 使用一种非常自然的语言 API 来编写您的断言,这将使您的测试更易于编写和改进。 这是通过将 getter 链接在一起来创建和执行断言来完成的,从而更容易将需求转换为代码:

let user = {name: 'Scott'};


expect(user).to.have.property('name');

请注意: 看看您如何用自然语言阅读断言并理解发生了什么? 这是使用像 Chai 这样的断言库的主要优势之一!

这些吸气剂的更多示例是:

  • to
  • be
  • is
  • and
  • has
  • have

这些 getter 中有相当一部分可以链接在一起并与断言方法一起使用,例如 true, ok, existempty 在一行中创建一些复杂的断言:

"use strict";

const expect = require('chai').expect;


expect({}).to.exist;
expect(26).to.equal(26);
expect(false).to.be.false;
expect('hello').to.be.string;


expect([1, 2, 3]).to.not.be.empty;


expect([1, 2, 3]).to.have.length.of.at.least(3);

请注意: 可以找到可用方法的完整列表 此处.

您可能还想查看可用列表 插件 为柴。 这些使得测试更复杂的功能变得更加容易。

采取 柴http 例如,这是一个帮助您测试服务器路由的插件:

"use strict";

const chai = require('chai');
const chaiHttp = require('chai-http');

chai.use(chaiHttp);

chai.request(app)
    .put('/api/auth')
    .send({username: '[email protected]', password: 'abc123'})
    .end(function(err, res) {
        expect(err).to.be.null;
        expect(res).to.have.status(200);
    });

用 Mocha 组织测试用例——describe() 和 it()

Mocha 是 Node 的测试框架,可让您灵活地串行运行异步(或同步)代码。 任何未捕获的异常都会与抛出异常的测试用例一起显示,以便于准确识别失败的原因和原因。

建议全局安装 Mocha:

$ npm install mocha -g

你会希望它是一个全局安装,因为 mocha 命令用于在本地目录中运行项目的测试。

这段代码有什么作用?

it() 应该返回 X。 it() 定义测试用例,Mocha 将运行每个 it() 作为单元测试。 要组织多个单元测试,我们可以 describe() 一个共同的功能,从而构建摩卡测试。

这可能最好用一个具体的例子来描述:

"use strict";
const expect = require('chai').expect;

describe('Math', function() {
    describe('#abs()', function() {
        it('should return positive value of given negative number', function() {
            expect(Math.abs(-5)).to.be.equal(5);
        });
    });
});

describe() 方法,我们定义了一个 测试名称,被称为 #abs(). 您也可以通过他们的名字单独运行测试——这将在后面介绍。

请注意: 使用 Mocha 测试,您不需要 require() 任何摩卡方法。 这些方法在运行时全局提供 mocha 命令。

为了运行这些测试,保存你的文件并使用 mocha 命令:

$ mocha .

  Math
    #abs()
      ✓ should return positive value of given number 


  1 passing (9ms)

输出是运行的测试及其结果的细分。 注意嵌套的方式 describe() 呼叫结转到结果输出。 将给定方法或功能的所有测试嵌套在一起很有用。

这些方法是 Mocha 测试框架的基础。 使用它们来编写和组织您喜欢的测试。 我们将在下一节中看到一个这样的例子。

使用 Mocha 和 Chai 编写测试

在项目中组织测试的推荐方法是将所有测试都放在自己的 /test 目录。 默认情况下,Mocha 使用 glob 检查单元测试 ./test/*.js./test/*.coffee. 从那里,它将加载并执行任何调用 describe() 方法。

查看我们的 Git 学习实践指南,其中包含最佳实践、行业认可的标准以及随附的备忘单。 停止谷歌搜索 Git 命令,实际上 学习 它!

通常在测试文件后缀 .test.js 对于包含 Mocha 测试的源文件:

├── package.json
├── lib
│   ├── db.js
│   ├── models.js
│   └── util.js
└── test
    ├── db.test.js
    ├── models.test.js
    ├── util.test.js
    └── util.js

util.js ,在 test 目录不包含任何单元测试,仅包含帮助测试的实用函数。

备注:您可以使用对您有意义的任何结构,单元测试会自动获取。

在实际编写测试时,使用 describe() 方法。 您可以按特性、功能、文件或任何其他任意级别组织它们。 例如,为描述功能级别的工作而组织的测试文件如下所示:

"use strict";

const expect = require('chai').expect;

describe('Math', function() {
    describe('#abs()', function() {
        it('should return positive value of given negative number', function() {
            expect(Math.abs(-5)).to.be.equal(5);
        });

        it('should return positive value of given positive number', function() {
            expect(Math.abs(3)).to.be.equal(3);
        });

        it('should return 0 given 0', function() {
            expect(Math.abs(0)).to.be.equal(0);
        });
    });
});

然后运行测试会给你输出:

$ mocha .

  Math
    #abs()
      ✓ should return positive value of given negative number 
      ✓ should return positive value of given positive number 
      ✓ should return 0 given 0 


  3 passing (11ms)

进一步扩展,您甚至可以在单个文件中测试多种方法。 在这种情况下,方法按 Math 宾语:

"use strict";

const expect = require('chai').expect;

describe('Math', function() {
    describe('#abs()', function() {
        it('should return positive value of given negative number', function() {
            expect(Math.abs(-5)).to.be.equal(5);
        });

        it('should return positive value of given positive number', function() {
            expect(Math.abs(3)).to.be.equal(3);
        });

        it('should return 0 given 0', function() {
            expect(Math.abs(0)).to.be.equal(0);
        });
    });

    describe('#sqrt()', function() {
        it('should return the square root of a given positive number', function() {
            expect(Math.sqrt(25)).to.be.equal(5);
        });

        it('should return NaN for a given negative number', function() {
            expect(Math.sqrt(-9)).to.be.NaN;
        });

        it('should return 0 given 0', function() {
            expect(Math.sqrt(0)).to.be.equal(0);
        });
    });
});

结果是:

$ mocha .

  Math
    #abs()
      ✓ should return positive value of given negative number 
      ✓ should return positive value of given positive number 
      ✓ should return 0 given 0 
    #sqrt()
      ✓ should return the square root of a given positive number 
      ✓ should return NaN for a given negative number 
      ✓ should return 0 given 0 


  6 passing (10ms)

Mocha Hooks – before(), after(), beforeEach() 和 afterEach()

不可否认,大多数单元测试并不是这么简单。 很多时候,您可能需要其他资源来执行测试,例如数据库或其他一些外部资源(或它们的模拟/存根)。 为了进行设置,我们可以使用以下一项或多项 摩卡钩 方法:

  • before():在给定块中的所有测试之前运行
  • beforeEach():在给定块中的每个测试之前运行
  • after():在给定块中的所有测试之后运行
  • afterEach():在给定块中的每个测试之后运行

这些挂钩是执行测试所需的设置和拆卸工作的理想场所。 一个常见的用例是在运行测试之前建立与数据库的连接:

"use strict";

const expect = require('chai').expect;
let User = require('../models').User;

describe('Users', function() {

    let database = null;

    before(function(done) {
        
    });

    afterEach(function(done) {
        
    });

    describe('#save()', function() {
        it('should save User data to database', function(done) {
            
        });
    });

    describe('#load()', function() {
        it('should load User data from database', function(done) {
            
        });
    });
});

之前 任何 的测试运行,功能发送到我们的 before() 方法运行(并且在整个测试中只运行一次),它建立到数据库的连接。 一旦完成,我们的测试套件就会运行。

由于我们不希望来自一个测试套件的数据影响我们的其他测试,因此我们需要在每个套件运行后从数据库中清除数据。 这是什么 afterEach() 是为了。 我们使用这个钩子来清除所有的数据库数据 测试用例已运行,因此我们可以从头开始进行下一次测试。

运行摩卡测试

对于大多数情况,这部分非常简单。 假设您已经安装了 Mocha 并导航到项目目录,大多数项目只需要使用 mocha 不带参数的命令来运行他们的测试:

$ mocha


  Math
    #abs()
      ✓ should return positive value of given negative number 
      ✓ should return positive value of given positive number 
      ✓ should return 0 given 0 
    #sqrt()
      ✓ should return the square root of a given positive number 
      ✓ should return NaN for a given negative number 
      ✓ should return 0 given 0 


  6 passing (10ms)

这与我们之前的示例略有不同,因为我们不需要告诉 Mocha 我们的测试位于何处。 在这个例子中,测试代码在预期的位置 /test.

但是,在运行测试时,您可能希望使用一些有用的选项。 例如,如果您的某些测试失败,您可能不希望每次进行更改时都运行整个套件。 对于某些项目,完整的测试套件可能需要几分钟才能完成。 如果您真的只需要运行一个测试,那会浪费很多时间。

对于这样的情况,你应该 告诉 Mocha 要运行哪些测试. 这可以使用 -g or -f 选项​​。

要运行单个测试,您可以提供 -g 标记并在要运行的测试之间添加通用模式。 例如,如果你想运行 #sqrt() 测试:

$ mocha -g sqrt

  Math
    #sqrt()
      ✓ should return the square root of a given positive number 
      ✓ should return NaN for a given negative number 
      ✓ should return 0 given 0 


  3 passing (10ms)

注意, #abs() 此运行中不包括测试。 如果您对测试名称进行了相应的规划,则可以使用此选项仅运行测试的特定部分。

然而,这些并不是唯一有用的选项。 以下是您可能想要查看的更多 Mocha 选项:

  • --invert:反转 -g-f 火柴
  • --recursive: 包括子目录
  • --harmony:启用 Node 中的所有和谐功能

备注:您可以使用 mocha -h 命令,或在 这页.

在哪里可以学到更多? 这个主题的内容远远超过我们在一篇简短的文章中涵盖的内容,所以如果您想了解更多信息,我们建议您查看官方 摩卡湾仔 文档。

结论

请记住,Mocha 和 Chai 都可以用于测试几乎任何类型的 Node 项目,无论是库、命令行工具,甚至是网站。 利用可用的各种选项和插件,您应该能够非常轻松地满足您的测试需求。 这些库中的每一个对于验证您的代码都非常有用,并且应该在几乎所有的 Node 项目中使用。

希望这是对 Mocha 和 Chai 的有用介绍。 除了我在这里介绍的内容之外,还有很多东西需要学习,因此请务必查看文档以获取更多信息。

时间戳记:

更多来自 堆栈滥用