在测试微服务功能时模拟数据库

时间:2017-05-26 10:25:55

标签: javascript node.js database postgresql pg-promise

我已经使用pg-promise创建了一个基本的NodeJS微服务。我已经通过express:

创建了包含API的路由文件(index.js)
var express = require('express');
var router = express.Router();

var db = require('./../queries');

router.get('/api/puppy', db.getAllPuppies);
router.post('/api/puppy', db.createPuppy);

module.exports = router;

然后我在queries.js文件中添加了两个函数:

var promise = require('bluebird');

var options = {
  promiseLib: promise
};

var config = require('./config.json');
var pgp = require('pg-promise')(options);
var connectionString = process.env.DB_PATH || "postgres://postgres:xxxx@localhost:5432/postgres";
var db = pgp(connectionString);


function getAllPuppies(req, res, next) {
  db.any('select * from puppy')
    .then(function (data) {
      res.status(200)
        .json({
          status: 'success',
          data: data,
        });
    })
    .catch(function (err) {
      return next(err);
    });
}


function createPuppy(req, res, next) {
  db.none('insert into ' +
    'puppy(name, description) ' +
    'values(${name}, ${description})',
    req.body)
    .then(function () {
      res.status(200)
        .json({
          status: 'success',
        });
    })
    .catch(function (err) {
      return next(err);
    });
}


module.exports = {
  getAllPuppies,
  createPuppy,
};

该应用程序运行良好。我想以这种或那种方式测试这两个函数,但我对数据库应该被模拟的方式(我使用Mocha和诸如beforeEach之类的函数)来检索或创建数据。

2 个答案:

答案 0 :(得分:2)

作为一般经验法则,您有两种选择:

  1. 使用依赖注入并发送模拟数据库服务
  2. 配置您的测试环境以使用测试数据库,以便您可以按原样使用数据库。
  3. 要么是一个潜在有用的测试。第一个选项更多的是单元测试,并要求您重构代码以使用依赖注入。然后,您的测试代码将注入某种模拟对象,该对象具有与您的实际数据库对象相同的API。如果你对这类事情不熟悉,谷歌将成为你的朋友:单位测试和控制反转在概念上是相当直接的,但可能会有很多细节被卡住。

    由于您还没有设置依赖注入,您可以做的下一件事是配置您的测试环境以使用某种测试数据库。这将使它更像是一个集成测试。您可以通过为测试设置不同的数据库来做到这一点,然后您(仍然)必须重构代码以根据您的环境调整数据库连接凭据。同样,它在概念上很简单,但可能会有很多细节迷失。

    如果您不熟悉代码测试,那么花一些时间阅读它并真正了解其工作方式是值得的。阅读有关控制反转和单元测试的主题。正确的代码测试对任何代码库都是一个巨大的好处,但是你必须从一开始就计划它,因为适当的控制反转(这是正确的代码测试所必需的)确实需要以不同的方式组织你的代码。

    编辑添加一些细节:

    一旦您计划并执行代码测试,就可以非常轻松,但是开始使用并不是一项小任务。最重要的问题是您的代码必须以实际规划未来代码测试的方式编写。在您的情况下,这意味着要么重构代码以使用实际依赖项注入(node.js支持),要么至少根据环境调整数据库连接。后者会更容易做,但它只允许有限的测试。最后,如果你想进行正确的代码测试,你将不得不重构依赖注入/控制反转。

    一旦你这样做,你的下一步将是选择一个测试框架。我不知道node.js的默认值是什么,但是大多数语言/框架都有一个相当标准的语言/框架,不应该很难找到。可能会有多个选项,因此您可能会花一些时间选择最适合您风格的选项。选择测试框架后,您将不得不阅读它的工作原理,如何设置以及如何使用它。

    完成这些工作后,您实际上可以开始测试了。第一次这样做时需要做很多工作,但这是值得的。

答案 1 :(得分:1)

你正在做的一些事情会产生一些问题,从长远来看,这将是一个真正的问题。然而,让我们接近最大的罪犯:在你的路线文件中声明你的数据库。这是一个大禁忌,它应该只被声明一次并在需要时被要求。 pg-promise here at the official demo的作者可以找到一个很好的例子。要注意的重点是分区化。如果您的设计不正确,那么测试就是您现在遇到的PITA。但是,如果你正确地划分它,那么当你编写测试时,你只需要传递模拟对象,并使用方法调用它,这将为你提供一个可测试的环境。

演示中最大的例子:

var options = {
   // Use a custom promise library, instead of the default ES6 Promise:
   promiseLib: promise,
   // Extending the database protocol with our custom repositories:
   extend: obj => {

     // Do not use 'require()' here, because this event occurs for every task
     // and transaction being executed, which should be as fast as possible.
     obj.users = repos.users(obj, pgp);
     obj.products = repos.products(obj, pgp);

     // Alternatively, you can set all repositories in a loop:
     //
     // for (var r in repos) {
     //    obj[r] = repos[r](obj, pgp);
     // }
    }

};

有了这个你现在可以在Mocha中编写简单的模拟(我建议查看unitjs作为一些例子,但是当你将某些内容传递给测试时,你只需要传递一个{{1}带有req对象且带有app的{​​{1}}对象的对象....你明白了。

因此,最重要的是你设计错误(你的代码看起来很好)。您需要将事物重构为单独的部分,因此当您进行测试时,您实际上可以编写有意义的测试。