简体   繁体   English

带有回调的 Sinon 测试函数调用

[英]Sinon test function call with a callback

I have a Connection class with a transaction function which is used to execute mysql transactions.我有一个带有事务函数的 Connection 类,用于执行 mysql 事务。 It takes a callback as a parameter which represents any mysql queries to be executed.它接受一个回调作为参数,代表要执行的任何 mysql 查询。 With the help of stackoverflow users I have created a unit test for the function itself, but I'm having trouble testing the lambda handler which actually uses the function.在 stackoverflow 用户的帮助下,我为函数本身创建了一个单元测试,但是我在测试实际使用该函数的 lambda 处理程序时遇到了问题。

Here is the Connection 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 + '\0')
            }
        });
    }

    async transaction(callback) {
        const connection = await this.pool.getConnection();
        await connection.beginTransaction();

        try {
            await callback(connection);
            await connection.commit();
        } catch (err) {
            await connection.rollback();
            console.log("An exception was thrown and the mysql transaction has been rolled back", err);
        } finally {
            connection.release();
        }
    }
}
module.exports = { Connection };

And here is the lambda handler.这是 lambda 处理程序。 I've only included the most relevant part我只包含了最相关的部分

const conns = require('./connection');

let response = {
  statusCode: 200,
  body: {
    message: 'SQS event processed.',
  },
};

exports.handler = async(event) => {
  console.log(event.Records);
  try {
    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]);
    });

  } catch (e) {
    console.log('There was an error while processing', { errorMessage: e});

    response = {
      statusCode: 400,
      body: e
    }
  }

  return response;
};

And here I am trying to unit test the handler:在这里,我试图对处理程序进行单元测试:

test('Should test handler without mocking', 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);

    // attempt to mock transaction function
    sinon.stub(conn, 'transaction').returns(results);

    const response = await index.handler(mocks.baseMessage, null);
    expect(response.statusCode).toBe(200);
});

The test seems to work up until I attempt to mock the conn.transaction call.在我尝试模拟 conn.transaction 调用之前,该测试似乎一直有效。 It just gives a ReferenceError: conn is not defined .它只是给出了一个ReferenceError: conn is not defined How can I mock the results of transaction so that the handler function will be covered by tests?如何模拟transaction的结果,以便测试覆盖处理程序函数?

Unit test solution:单元测试解决方案:

handler.js : handler.js

const conns = require('./connection');

let response = {
  statusCode: 200,
  body: {
    message: 'SQS event processed.',
  },
};

exports.handler = async (event) => {
  console.log(event.Records);
  const options = {};
  const [values1, values2, values3] = [1, 2, 3];
  try {
    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]);
    });
  } catch (e) {
    console.log('There was an error while processing', { errorMessage: e });

    response = {
      statusCode: 400,
      body: e,
    };
  }

  return response;
};

handler.test.js : handler.test.js :

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

const mocks = {
  baseMessage: {
    Records: [],
  },
};

describe('64310254', () => {
  it('Should test handler without mocking', async () => {
    const poolConnectionStub = {
      query: sinon.stub(),
    };
    const connectionStub = {
      createPool: sinon.stub(),
      transaction: sinon.stub().callsFake(async (callback) => {
        await callback(poolConnectionStub);
      }),
    };
    const ConnectionStub = sinon.stub(conns, 'Connection').returns(connectionStub);

    const response = await index.handler(mocks.baseMessage, null);
    expect(response.statusCode).to.be.eq(200);
    sinon.assert.calledOnceWithExactly(ConnectionStub, {});
    sinon.assert.calledOnce(connectionStub.createPool);
    sinon.assert.calledOnceWithExactly(connectionStub.transaction, sinon.match.func);
    sinon.assert.calledThrice(poolConnectionStub.query);
    ConnectionStub.restore();
  });

  it('should handle error', async () => {
    const poolConnectionStub = {
      query: sinon.stub(),
    };
    const connectionStub = {
      createPool: sinon.stub(),
      transaction: sinon.stub().rejects(new Error('timeout')),
    };
    const ConnectionStub = sinon.stub(conns, 'Connection').returns(connectionStub);

    const response = await index.handler(mocks.baseMessage, null);
    expect(response.statusCode).to.be.eq(400);
    sinon.assert.calledOnceWithExactly(ConnectionStub, {});
    sinon.assert.calledOnce(connectionStub.createPool);
    sinon.assert.calledOnceWithExactly(connectionStub.transaction, sinon.match.func);
    sinon.assert.notCalled(poolConnectionStub.query);
    ConnectionStub.restore();
  });
});

unit test result with coverage report:带有覆盖率报告的单元测试结果:

  64310254
[]
    ✓ Should test handler without mocking
[]
There was an error while processing { errorMessage:
   Error: timeout
       at Context.it (/Users/ldu020/workspace/github.com/mrdulin/expressjs-research/src/stackoverflow/64310254/handler.test.js:40:41)
       at callFn (/Users/ldu020/workspace/github.com/mrdulin/expressjs-research/node_modules/mocha/lib/runnable.js:387:21)
       at Test.Runnable.run (/Users/ldu020/workspace/github.com/mrdulin/expressjs-research/node_modules/mocha/lib/runnable.js:379:7)
       at Runner.runTest (/Users/ldu020/workspace/github.com/mrdulin/expressjs-research/node_modules/mocha/lib/runner.js:535:10)
       at /Users/ldu020/workspace/github.com/mrdulin/expressjs-research/node_modules/mocha/lib/runner.js:653:12
       at next (/Users/ldu020/workspace/github.com/mrdulin/expressjs-research/node_modules/mocha/lib/runner.js:447:14)
       at /Users/ldu020/workspace/github.com/mrdulin/expressjs-research/node_modules/mocha/lib/runner.js:457:7
       at next (/Users/ldu020/workspace/github.com/mrdulin/expressjs-research/node_modules/mocha/lib/runner.js:362:14)
       at Immediate.<anonymous> (/Users/ldu020/workspace/github.com/mrdulin/expressjs-research/node_modules/mocha/lib/runner.js:425:5)
       at runCallback (timers.js:705:18)
       at tryOnImmediate (timers.js:676:5)
       at processImmediate (timers.js:658:5) }
    ✓ should handle error


  2 passing (42ms)

---------------|---------|----------|---------|---------|-------------------
File           | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
---------------|---------|----------|---------|---------|-------------------
All files      |   63.64 |        0 |   28.57 |   65.63 |                   
 connection.js |   14.29 |        0 |       0 |   15.38 | 5-32              
 handler.js    |     100 |      100 |     100 |     100 |                   
---------------|---------|----------|---------|---------|-------------------

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM