我有一个小的包装器类,它向一些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属性的最佳方法是什么,这样我就可以轻松地声明针对它的方法调用?任何帮助将不胜感激
您不需要使用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
方法的测试方法相同。