Sinon 模拟一个接受回调参数的函数
Sinon mock a function that accepts a callback parameter
我有一个连接 class 用于创建 Mysql 连接池并执行事务。 class 的方法 transaction
接受回调函数作为参数。传递给 transaction
函数的回调将是 mysql2.query
Inserts.
连接class:
const mysql2 = require('mysql2/promise');
class Connection {
constructor(options = {}) {
this.options = options;
}
createPool () {
this.pool = mysql2.createPool({
host: this.options.host,
user: this.options.user,
database: 'my_db',
ssl: 'Amazon RDS',
password: this.options.password,
authPlugins: {
mysql_clear_password: () => () => Buffer.from(this.options.password + '[=10=]')
}
});
}
async transaction(callback) {
const connection = await this.pool.getConnection();
await connection.beginTransaction();
try {
await callback(connection);
await connection.commit();
} catch (err) {
await connection.rollback();
throw err;
} finally {
connection.release();
}
}
}
module.exports = { Connection };
下面是 transaction
函数的使用方法。
await conn.transaction(async connection => {
await connection.query(sql1,[values1]);
await connection.query(sql2,[values2]);
await connection.query(sql3,[values3]);
});
我的目标是模拟 async transaction
方法,但我很难处理回调。这是我根据对类似帖子的一些答案和阅读 sinon 文档所做的尝试。
https://sinonjs.org/releases/v9.2.0/stubs/
it('should test transaction function in Connection', async () => {
jest.setTimeout(30000);
const results = { affectedRows: 1 };
const poolStub = {
getConnection: sinon.stub().returnsThis(),
query: sinon.stub().returns(results),
beginTransaction: sinon.stub().returnsThis(),
release: sinon.stub(),
};
const createPoolStub = sinon.stub(mysql2, 'createPool').returns(poolStub);
const conn = new conns.Connection();
await conn.createPool();
const actual = await conn.transaction('select 1 + 1 as solution',[]);
expect(actual).to.be.eql(1);
sinon.assert.calledOnce(createPoolStub);
sinon.assert.calledOnce(poolStub.getConnection);
sinon.assert.calledWithExactly(poolStub.query, 'select 1 + 1 as solution');
sinon.assert.calledOnce(poolStub.release);
});
请注意,当 query
方法被调用时,我正在尝试 return results = { affectedRows: 1 };
。然而,这不能正常工作,查询尝试只是超时。
: Timeout - Async callback was not invoked within the 30000ms timeout specified by jest.setTimeout.
根据要求,这里是处理程序代码,显示了如何使用连接 class,包括如何导入和初始化它
const utils = require('./utils');
const conns = require('./connection');
let response = {
statusCode: 200,
body: {
message: 'SQS event processed.',
},
};
exports.handler = async(event) => {
try {
const values1 = [];
const values2= [];
const values3 = [];
for (const currentMessage of event.Records) {
const data = JSON.parse(currentMessage.body);
console.log(`Processing Received data`);
const {record} = data;
if (record.id == 100) {
values1.push([record.field1,record.field2, record.field3, Date.now(), Date.now(), 'service-user', 'service-user']);
}
if (record.id == 200) {
values2.push([record.field1,record.field2, record.field3, Date.now(), Date.now(), 'service-user', 'service-user']);
}
if (record.id == 300) {
values3.push([record.field1,record.field2, record.field3, Date.now(), Date.now(), 'service-user', 'service-user']);
}
}
const options = {
host: 'my-host',
user: 'service-user'
};
const token = utils.getToken(options);
options.password = token;
const conn = new conns.Connection(options);
conn.createPool();
const sql1 = 'INSERT INTO table1(field1, field2, field4, created_date, modified_date, created_by, modified_by) VALUES ?';
const sql2 = 'INSERT INTO table2(field1, field2, field4, created_date, modified_date, created_by, modified_by) VALUES ?';
const sql3 = 'INSERT INTO table3(field1, field2, field4, created_date, modified_date, created_by, modified_by) VALUES ?';
await conn.transaction(async connection => {
await connection.query(sql1,[values1]);
await connection.query(sql2,[values2]);
await connection.query(sql3,[values3]);
});
await conn.pool.end();
console.log("Connection ended")
} catch (e) {
console.log('There was an error while processing', { errorMessage: e});
response = {
statusCode: 400,
body: e
}
}
return response;
};
这是单元测试解决方案:
connection.test.js
:
const conns = require('./connection');
const sinon = require('sinon');
const mysql2 = require('mysql2/promise');
describe('64255673', () => {
it('should test transaction function in Connection', async () => {
const results = { affectedRows: 1 };
const connectionStub = {
beginTransaction: sinon.stub(),
commit: sinon.stub(),
rollback: sinon.stub(),
release: sinon.stub(),
};
const poolStub = {
getConnection: sinon.stub().returns(connectionStub),
query: sinon.stub().returns(results),
};
const createPoolStub = sinon.stub(mysql2, 'createPool').returns(poolStub);
const conn = new conns.Connection();
conn.createPool();
const callback = sinon.stub();
await conn.transaction(callback);
sinon.assert.calledOnce(createPoolStub);
sinon.assert.calledOnce(poolStub.getConnection);
sinon.assert.calledOnce(connectionStub.beginTransaction);
sinon.assert.calledOnceWithExactly(callback, connectionStub);
sinon.assert.calledOnce(connectionStub.commit);
sinon.assert.calledOnce(connectionStub.release);
});
});
带有覆盖率报告的单元测试结果:
64255673
✓ should test transaction function in Connection
1 passing (13ms)
---------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
---------------|---------|----------|---------|---------|-------------------
All files | 71.43 | 100 | 60 | 76.92 |
connection.js | 71.43 | 100 | 60 | 76.92 | 16,29-30
---------------|---------|----------|---------|---------|-------------------
您可以使用stub.rejects(value);
来使await connection.commit()
被拒绝。然后你可以测试catch
块中的代码。
我有一个连接 class 用于创建 Mysql 连接池并执行事务。 class 的方法 transaction
接受回调函数作为参数。传递给 transaction
函数的回调将是 mysql2.query
Inserts.
连接class:
const mysql2 = require('mysql2/promise');
class Connection {
constructor(options = {}) {
this.options = options;
}
createPool () {
this.pool = mysql2.createPool({
host: this.options.host,
user: this.options.user,
database: 'my_db',
ssl: 'Amazon RDS',
password: this.options.password,
authPlugins: {
mysql_clear_password: () => () => Buffer.from(this.options.password + '[=10=]')
}
});
}
async transaction(callback) {
const connection = await this.pool.getConnection();
await connection.beginTransaction();
try {
await callback(connection);
await connection.commit();
} catch (err) {
await connection.rollback();
throw err;
} finally {
connection.release();
}
}
}
module.exports = { Connection };
下面是 transaction
函数的使用方法。
await conn.transaction(async connection => {
await connection.query(sql1,[values1]);
await connection.query(sql2,[values2]);
await connection.query(sql3,[values3]);
});
我的目标是模拟 async transaction
方法,但我很难处理回调。这是我根据对类似帖子的一些答案和阅读 sinon 文档所做的尝试。
https://sinonjs.org/releases/v9.2.0/stubs/
it('should test transaction function in Connection', async () => {
jest.setTimeout(30000);
const results = { affectedRows: 1 };
const poolStub = {
getConnection: sinon.stub().returnsThis(),
query: sinon.stub().returns(results),
beginTransaction: sinon.stub().returnsThis(),
release: sinon.stub(),
};
const createPoolStub = sinon.stub(mysql2, 'createPool').returns(poolStub);
const conn = new conns.Connection();
await conn.createPool();
const actual = await conn.transaction('select 1 + 1 as solution',[]);
expect(actual).to.be.eql(1);
sinon.assert.calledOnce(createPoolStub);
sinon.assert.calledOnce(poolStub.getConnection);
sinon.assert.calledWithExactly(poolStub.query, 'select 1 + 1 as solution');
sinon.assert.calledOnce(poolStub.release);
});
请注意,当 query
方法被调用时,我正在尝试 return results = { affectedRows: 1 };
。然而,这不能正常工作,查询尝试只是超时。
: Timeout - Async callback was not invoked within the 30000ms timeout specified by jest.setTimeout.
根据要求,这里是处理程序代码,显示了如何使用连接 class,包括如何导入和初始化它
const utils = require('./utils');
const conns = require('./connection');
let response = {
statusCode: 200,
body: {
message: 'SQS event processed.',
},
};
exports.handler = async(event) => {
try {
const values1 = [];
const values2= [];
const values3 = [];
for (const currentMessage of event.Records) {
const data = JSON.parse(currentMessage.body);
console.log(`Processing Received data`);
const {record} = data;
if (record.id == 100) {
values1.push([record.field1,record.field2, record.field3, Date.now(), Date.now(), 'service-user', 'service-user']);
}
if (record.id == 200) {
values2.push([record.field1,record.field2, record.field3, Date.now(), Date.now(), 'service-user', 'service-user']);
}
if (record.id == 300) {
values3.push([record.field1,record.field2, record.field3, Date.now(), Date.now(), 'service-user', 'service-user']);
}
}
const options = {
host: 'my-host',
user: 'service-user'
};
const token = utils.getToken(options);
options.password = token;
const conn = new conns.Connection(options);
conn.createPool();
const sql1 = 'INSERT INTO table1(field1, field2, field4, created_date, modified_date, created_by, modified_by) VALUES ?';
const sql2 = 'INSERT INTO table2(field1, field2, field4, created_date, modified_date, created_by, modified_by) VALUES ?';
const sql3 = 'INSERT INTO table3(field1, field2, field4, created_date, modified_date, created_by, modified_by) VALUES ?';
await conn.transaction(async connection => {
await connection.query(sql1,[values1]);
await connection.query(sql2,[values2]);
await connection.query(sql3,[values3]);
});
await conn.pool.end();
console.log("Connection ended")
} catch (e) {
console.log('There was an error while processing', { errorMessage: e});
response = {
statusCode: 400,
body: e
}
}
return response;
};
这是单元测试解决方案:
connection.test.js
:
const conns = require('./connection');
const sinon = require('sinon');
const mysql2 = require('mysql2/promise');
describe('64255673', () => {
it('should test transaction function in Connection', async () => {
const results = { affectedRows: 1 };
const connectionStub = {
beginTransaction: sinon.stub(),
commit: sinon.stub(),
rollback: sinon.stub(),
release: sinon.stub(),
};
const poolStub = {
getConnection: sinon.stub().returns(connectionStub),
query: sinon.stub().returns(results),
};
const createPoolStub = sinon.stub(mysql2, 'createPool').returns(poolStub);
const conn = new conns.Connection();
conn.createPool();
const callback = sinon.stub();
await conn.transaction(callback);
sinon.assert.calledOnce(createPoolStub);
sinon.assert.calledOnce(poolStub.getConnection);
sinon.assert.calledOnce(connectionStub.beginTransaction);
sinon.assert.calledOnceWithExactly(callback, connectionStub);
sinon.assert.calledOnce(connectionStub.commit);
sinon.assert.calledOnce(connectionStub.release);
});
});
带有覆盖率报告的单元测试结果:
64255673
✓ should test transaction function in Connection
1 passing (13ms)
---------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
---------------|---------|----------|---------|---------|-------------------
All files | 71.43 | 100 | 60 | 76.92 |
connection.js | 71.43 | 100 | 60 | 76.92 | 16,29-30
---------------|---------|----------|---------|---------|-------------------
您可以使用stub.rejects(value);
来使await connection.commit()
被拒绝。然后你可以测试catch
块中的代码。