使用 sinon 进行回调的单元测试 mysql 查询

Unit test mysql query with callback using sinon

我有 class 用于创建 mysql 连接池并在数据库中插入记录。插入工作正常,但我无法通过单元测试获得全面覆盖。

这里是连接 class:

const mysql2 = require('mysql2');

class Connection {
    constructor(options = {}) {
        this.options = options;
    }

    createPool () {
        this.pool = mysql2.createPool({
            host: this.options.host,
            user: this.options.user,
            database:  'my_database',
            ssl: 'Amazon RDS',
            password: this.options.password,
            authPlugins: {
                mysql_clear_password: () => () => Buffer.from(this.options.password + '[=11=]')
            }
        });
    }

    async insert (sql, values) {
        const promise = new Promise( (resolve, reject) => {

            try {
                this.pool.query(sql, [values],function (error, results, fields) {
                    if (error) throw error;
                    console.log(results.affectedRows); //Show 1
                    resolve (results.affectedRows);
                });
            } catch (e) {
                reject(e);
            }

        })
        return promise;
    }
}

module.exports = { Connection };

这是我的失败测试:

const conns = require('../src/connection');
const sinon = require('sinon');
const mysql2 = require('mysql2');

describe('handler', () => {

    test('Test insert from Connection', async () => {
        const options = {
            host: 'testHost',
            user: 'testUser',
            password: 'testPassword'
        };

        const poolStub = {
            getConnection: sinon.stub().returnsThis(),
            query: sinon.stub().returnsThis(),
        };

        const createPoolStub = sinon.stub(mysql2, 'createPool').returns(poolStub);
        const conn = new conns.Connection(options);
        await conn.createPool();
        await conn.insert( 'select 1 + 1 as solution', [])

        sinon.restore();
    });
});

测试刚刚超时。

: Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.

我很确定我没有在 poolStub 中正确存根 query,但我不确定如何解释回调方法。我如何对这个 insert 方法进行单元测试,以便我可以全面覆盖?

单元测试解决方案如下:

connection.js:

const mysql2 = require('mysql2');

class Connection {
  constructor(options = {}) {
    this.options = options;
  }

  createPool() {
    this.pool = mysql2.createPool({
      host: this.options.host,
      user: this.options.user,
      database: 'my_database',
      ssl: 'Amazon RDS',
      password: this.options.password,
      authPlugins: {
        mysql_clear_password: () => () => Buffer.from(this.options.password + '[=10=]'),
      },
    });
  }

  async insert(sql, values) {
    const promise = new Promise((resolve, reject) => {
      try {
        this.pool.query(sql, [values], function(error, results, fields) {
          if (error) throw error;
          console.log(results.affectedRows); //Show 1
          resolve(results.affectedRows);
        });
      } catch (e) {
        reject(e);
      }
    });
    return promise;
  }
}

module.exports = { Connection };

connection.test.js:

const conns = require('./connection');
const sinon = require('sinon');
const mysql2 = require('mysql2');
const { expect } = require('chai');

describe('handler', () => {
  it('Test insert from Connection', async () => {
    const options = {
      host: 'testHost',
      user: 'testUser',
      password: 'testPassword',
    };

    const results = { affectedRows: 1 };
    const poolStub = {
      getConnection: sinon.stub().returnsThis(),
      query: sinon.stub().callsFake((sql, values, callback) => {
        callback(null, results);
      }),
    };

    const createPoolStub = sinon.stub(mysql2, 'createPool').returns(poolStub);
    const conn = new conns.Connection(options);
    conn.createPool();
    const actual = await conn.insert('select 1 + 1 as solution', []);
    expect(actual).to.be.eql(1);
    sinon.assert.calledWithExactly(poolStub.query, 'select 1 + 1 as solution', [[]], sinon.match.func);
    sinon.assert.calledWithExactly(createPoolStub, {
      host: 'testHost',
      user: 'testUser',
      password: 'testPassword',
      database: 'my_database',
      ssl: 'Amazon RDS',
      authPlugins: {
        mysql_clear_password: sinon.match.func,
      },
    });
    sinon.restore();
  });
});

带有覆盖率报告的单元测试结果:

  handler
1
    ✓ Test insert from Connection


  1 passing (15ms)

---------------|---------|----------|---------|---------|-------------------
File           | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
---------------|---------|----------|---------|---------|-------------------
All files      |   73.33 |    33.33 |   71.43 |   84.62 |                   
 connection.js |   73.33 |    33.33 |   71.43 |   84.62 | 16,30