使用mysql和bluebird承诺设置一个带有sinon模拟的mocha测试

时间:2016-02-05 15:15:11

标签: javascript promise mocha bluebird node-mysql

我有一个带有以下设置的项目:JavaScript ES6(使用Babel编译),mocha测试,使用node-mysql和Bluebird Promises访问MySql。

也许将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');

  });
});

也许根本不可能嘲笑一个满意的对象?

有人在这方面有过这方面的经验吗?

1 个答案:

答案 0 :(得分:2)

DBRepository很难测试,因为有一点太多 - 为了使测试更容易,你需要分开一些问题。至少你需要将业务逻辑(原始SQL查询)分解为他们自己的类,如下所示:

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);
  }
}

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

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那样使用魔法。相反,保持简单,只是&#34;存根&#34;您需要在依赖项中测试的方法。但是,您可以使用&#34;间谍&#34;检查是否正在生成正确的SQL,但我在集成测试中这样做。

这有帮助吗?