使用 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
我有 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