使用 sinon 模拟设置 mocha 测试,使用 mysql 和 bluebird promises

Set up a mocha tests with sinon mocks, with mysql and bluebird promises

我给她一个项目,设置如下:JavaScript ES6(使用 Babel 转译)、mocha 测试、MySql 使用 node-mysql 和 Bluebird Promises 访问。

也许将 Bluebird 与 Babel/ES6 一起使用已经是我的第一个问题,但让我们解释一下情况和问题:

我的 DBRepository 对象:

let XDate = require('xdate'),
  _ = require('lodash');
const Promise = require("bluebird");
const debug = require('debug')('DBRepository');

class DBRepository {

  constructor(mysqlMock) {
    "use strict";
    this.mysql = mysqlMock;
    if( this.mysql == undefined) {
      debug('init mysql');
      this.mysql = require("mysql");
      Promise.promisifyAll(this.mysql);
      Promise.promisifyAll(require("mysql/lib/Connection").prototype);
      Promise.promisifyAll(require("mysql/lib/Pool").prototype);
    }

    this.config = {
      connectionLimit: 10,
      driver: 'pdo_mysql',
      host: 'my_sql_container',
      port: 3306,
      user: 'root',
      password: '**********',
      testDbName: 'db-name'
    };
    this.pool = this.mysql.createPool(this.config); // <== Here the error is thrown
  }

  getSqlConnection() {
    return this.pool.getConnectionAsync().disposer(function (connection) {
      try {
        connection.release();
      } catch (e) {
        debug('Error on releasing MySQL connection: ' + e);
        debug(e.stack);
      }
    });
  }

  getGoods(queryParams) {
    "use strict";

    if (queryParams === undefined) {
      queryParams = {};
    }
    if (queryParams.rowCount === undefined) {
      queryParams.rowCount = 15;
    }

    let query = "SELECT id, name FROM my_table";
    return Promise.using(this.getSqlConnection(), (conn => {
      debug('query: ' + query);
      return conn.queryAsync(query);
    }));
  }
}

这段代码在我的普通代码中对我来说工作正常,但是当我尝试在 mocha 测试中使用 int 时,使用 sinon 进行模拟我得到以下错误 TypeError: this.mysql.createPool is not a function

这是我的测试代码:

let expect = require('chai').expect,
  XDate = require('xdate'),
  _ = require('lodash'),
  sinon = require('sinon'),
  Promise = require('bluebird'),
  toBeMocketMySql = require('mysql');

Promise.promisifyAll(toBeMocketMySql);
Promise.promisifyAll(require("mysql/lib/Connection").prototype);
Promise.promisifyAll(require("mysql/lib/Pool").prototype);

describe(".inflateOffers(offerPCs, offerGroups)", () => {
  "use strict";

  it('should inflate Offers (with all OfferGroups and a PricingCluster from db rows.', () => {

    let offerPCs = JSON.parse('[... some objects ...]');
    let offerGroups = JSON.parse('[... some objects ...]');
    let mock = sinon.mock(toBeMocketMySql);
    let dbRepo = new DBRepository(mock); // <== Here the error is thrown


    let offers = dbRepo.inflateObjects(offerPCs, offerGroups);
    expect(offers).to.be.an('object')
      .and.to.be.an('array')
      .to.have.length(1);

    expect(offers[0]).to.be.an('object')
      .and.not.to.be.an('array')
      .to.have.property('a')
      .to.have.property('b');

  });
});

也许根本不可能模拟一个 promisfyed 对象?

有人在这方面有经验吗?

DBRepository 很难测试,因为发生的事情太多了 - 为了使测试更容易,您需要分离一些关注点。至少你需要将你的业务逻辑(原始 SQL 查询)分解成它们自己的 class,像这样:

class GoodsService {
  /**
   * Constructor - inject the database connection into the service.
   *
   * @param {object} db - A db connection
   */
  constructor(db) {
    this.db = db;
  }

  getGoods(queryParams) {
    if (queryParams === undefined) {
      queryParams = {};
    }
    if (queryParams.rowCount === undefined) {
      queryParams.rowCount = 15;
    }

    let query = "SELECT id, name FROM my_table";
    debug('query: ' + query);

    return this.db.queryAsync(query);
  }
}

现在您已经将业务逻辑与数据库连接器的设置分开了。您可以将完全实例化的数据库连接或存根传递到您的服务 class 测试中,如下所示:

let assert = require('assert');

describe('GoodsService', () => {
  it('should return an array', () => {
    let stubbedDb = {
      queryAsync: () => {
        return Promise.resolve([]);
      }
    };
    let instance = new GoodsService(stubbedDb);

    return instance.getGoods()
      .then((result) => {
        assert(Array.isArray(result), 'should return an array of something');
      });
  });
});

这有点过于简单化了,但您应该明白了。但是,有一些事情需要注意。

你不需要像 Chai 这样花哨的东西来测试承诺。 Mocha 已经对此提供了良好的内置支持。

你不需要像sinon.mock那样使用魔法。相反,保持简单,只需要 "stub" 需要在依赖项中测试的方法。但是,您可以使用 "spy" 来检查是否生成了正确的 SQL,但我会在集成测试中这样做。

有帮助吗?