使用mocha / sinon模拟es6类构造函数属性

问题描述 投票:0回答:1

我有一个小的包装器类,它向一些mysql功能添加了承诺。

const mysql = require('mysql');


export default class MySQL {
    constructor(host, user, password, database, port = 3306) {
        this.conn = mysql.createConnection({
            host,
            port,
            user,
            password,
            database,
        });
    }

    query(sql, args) {
        return new Promise((resolve, reject) => {
            // eslint-disable-next-line consistent-return
            this.conn.query(sql, args, (err, rows) => {
                if (err) {
                    reject(err);
                    return;
                }
                resolve(rows);
            });
        });
    }

    close() {
        return new Promise((resolve, reject) => {
            this.conn.end((err) => {
                if (err) {
                    reject(err);
                    return;
                }
                resolve();
            });
        });
    }
}

我正在尝试为此类编写一个单元测试,但是尝试模拟this.conn完全陷入困境。

我已经尝试了proxyquire和sinon的各种混合,并且两者都结合在一起。当我在beforeEach挂钩中使用proxyquire时:

beforeEach(function () {
    createConnectionStub = sinon.stub();
    MySQL = proxyquire('../../lib/utils/mysql', {
        mysql: {
            createConnection: createConnectionStub,
        },
    }).default;
});

并尝试将一个存根设置为conn对象:

it('Returns query results', async function () {
            stubDb = new MySQL('host', 'user', 'password', 'database');
            stubDb.conn = sinon.stub();

            const results = await stubDb.query('SELECT * FROM whatever');
        });

我不断得到TypeError: this.conn.query is not a function

将模拟程序设置为this.conn属性的最佳方法是什么,这样我就可以轻松地声明针对它的方法调用?任何帮助将不胜感激

node.js unit-testing mocha sinon
1个回答
0
投票

您不需要使用proxyquire模块,该模块主要用于模拟/存根来自模块的独立功能。单元测试应该是:

index.js

const mysql = require('mysql');

export default class MySQL {
  conn;
  constructor(host, user, password, database, port = 3306) {
    this.conn = mysql.createConnection({
      host,
      port,
      user,
      password,
      database,
    });
  }

  query(sql, args) {
    return new Promise((resolve, reject) => {
      // eslint-disable-next-line consistent-return
      this.conn.query(sql, args, (err, rows) => {
        if (err) {
          reject(err);
          return;
        }
        resolve(rows);
      });
    });
  }

  close() {
    return new Promise((resolve, reject) => {
      this.conn.end((err) => {
        if (err) {
          reject(err);
          return;
        }
        resolve();
      });
    });
  }
}

index.test.js

import MySQL from '.';
import sinon from 'sinon';
import chai from 'chai';
import chaiAsPromised from 'chai-as-promised';
chai.use(chaiAsPromised);
const expect = chai.expect;
const mysql = require('mysql');

describe('62124221', () => {
  afterEach(() => {
    sinon.restore();
  });
  it('should return query results', async () => {
    const mRows = [1, 2];
    const mConn = {
      query: sinon.stub().callsFake((sql, args, callback) => {
        callback(null, mRows);
      }),
    };
    sinon.stub(mysql, 'createConnection').returns(mConn);
    const db = new MySQL('host', 'user', 'password', 'database');
    const actual = await db.query('select 1;', 'args');
    expect(actual).to.be.deep.equal([1, 2]);
    sinon.assert.calledWithExactly(mysql.createConnection, {
      host: 'host',
      port: 3306,
      user: 'user',
      password: 'password',
      database: 'database',
    });
    sinon.assert.calledWithExactly(mConn.query, 'select 1;', 'args', sinon.match.func);
  });

  it('should return handle error', async () => {
    const mError = new Error('network');
    const mConn = {
      query: sinon.stub().callsFake((sql, args, callback) => {
        callback(mError);
      }),
    };
    sinon.stub(mysql, 'createConnection').returns(mConn);
    const db = new MySQL('host', 'user', 'password', 'database');
    await expect(db.query('select 1;', 'args')).to.be.rejectedWith('network');
    sinon.assert.calledWithExactly(mysql.createConnection, {
      host: 'host',
      port: 3306,
      user: 'user',
      password: 'password',
      database: 'database',
    });
    sinon.assert.calledWithExactly(mConn.query, 'select 1;', 'args', sinon.match.func);
  });
});

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

  62124221
    ✓ should return query results
    ✓ should return handle error


  2 passing (20ms)

----------|---------|----------|---------|---------|-------------------
File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
----------|---------|----------|---------|---------|-------------------
All files |      60 |       60 |   57.14 |      60 |                   
 index.ts |      60 |       60 |   57.14 |      60 | 29-35             
----------|---------|----------|---------|---------|-------------------

这里仅演示如何测试query方法,close方法的测试方法相同。

© www.soinside.com 2019 - 2024. All rights reserved.