Search code examples
mysqlnode.jsaws-lambdachaisinon

Unit test Mysql without connecting to database with NodeJS


I have a Conn class that is used to connect to a MySQL database using AWS IAM Authentication and run a query. I have the class written and it is working correctly, but I'm having a lot of difficulties trying to test it without hitting a database.

Here is the Conn Class:

const AWS = require('aws-sdk');
const mysql = require("mysql2/promise");

class Conn {
    constructor(options = {}) {
        this.options = options;
    }

    async getPool() {
        return mysql.createPool(this.options);
    }

    setToken () {
        this.signer = new AWS.RDS.Signer({
            region: 'us-east-1', // example: us-east-2
            hostname: this.options.host,
            port: 3306,
            username: this.options.user
        });

        this.token = this.signer.getAuthToken({
            username: this.options.user
        });
    }

    async setConnection () {
        this.dbOptions = {
            host     : this.options.host,
            user     : this.options.user,
            ssl: 'Amazon RDS',
            password: this.token,
            authPlugins: {
                mysql_clear_password: () => () => Buffer.from(this.token + '\0')
            }
        };

        this.pool = await this.getPool(this.dbOptions);
        this.conn = await this.pool.getConnection();
    }

    async executeQuery () {
        this.dbResult = await this.conn.query("select 1 + 1 as solution");
        this.conn.release();
    }
}

module.exports = {
    Conn: Conn
}

And here I'm trying to test the Conn.executeQuery function using sinon:

const { handler } = require("../src/conn");
const sinon = require("sinon");
const {expect: expects} = require("chai");
const connection = require('../src/conn');

describe("conn", () => {
    afterEach(() => {
        sinon.restore();
    });
    it("should test conn.executeQuery", async () => {
        const connStub = { query: sinon.stub().resolves({ rowCount: 1 }), release: sinon.stub() };
        const poolStub = { getConnection: sinon.stub().resolves(connStub) };
        const pool = {getPool: sinon.stub().resolves(poolStub)};
        const conn = new connection.Conn();
        await conn.setConnection();
        const actual = await conn.executeQuery()
        expects(actual).to.be.eql({ rowCount: 1 });
        sinon.assert.calledWith(connStub.query, "select 1 + 1 as solution");
        sinon.assert.calledOnce(connStub.release);
    });
});

Unfortunately, all this code produces is errors, such as

Error: connect ECONNREFUSED 127.0.0.1:3306

How can I test the Conn.executeQuery function using sinon and not hit a database?


Solution

  • unit test solution:

    Conn.js:

    const mysql = require('mysql2/promise');
    
    class Conn {
      constructor(options = {}) {
        this.options = options;
      }
    
      async getPool() {
        return mysql.createPool(this.options);
      }
    
      async setConnection() {
        this.dbOptions = {
          host: this.options.host,
          user: this.options.user,
          ssl: 'Amazon RDS',
          password: this.token,
          authPlugins: {
            mysql_clear_password: () => () => Buffer.from(this.token + '\0'),
          },
        };
    
        this.pool = await this.getPool(this.dbOptions);
        this.conn = await this.pool.getConnection();
      }
    
      async executeQuery() {
        this.dbResult = await this.conn.query('select 1 + 1 as solution');
        this.conn.release();
      }
    }
    
    module.exports = { Conn };
    

    Conn.test.js:

    const { Conn } = require('./Conn');
    const sinon = require('sinon');
    const mysql = require('mysql2/promise');
    
    describe('64112250', () => {
      afterEach(() => {
        sinon.restore();
      });
      it('should test conn.executeQuery', async () => {
        const poolStub = {
          getConnection: sinon.stub().returnsThis(),
          query: sinon.stub().returnsThis(),
          release: sinon.stub(),
        };
        const createPoolStub = sinon.stub(mysql, 'createPool').returns(poolStub);
        const conn = new Conn();
        await conn.setConnection();
        await conn.executeQuery();
        sinon.assert.calledOnce(createPoolStub);
        sinon.assert.calledOnce(poolStub.getConnection);
        sinon.assert.calledWithExactly(poolStub.query, 'select 1 + 1 as solution');
        sinon.assert.calledOnce(poolStub.release);
      });
    });
    

    unit test result with coverage report:

      64112250
        ✓ should test conn.executeQuery
    
    
      1 passing (24ms)
    
    ----------|---------|----------|---------|---------|-------------------
    File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
    ----------|---------|----------|---------|---------|-------------------
    All files |   81.82 |      100 |   66.67 |      90 |                   
     Conn.js  |   81.82 |      100 |   66.67 |      90 | 19                
    ----------|---------|----------|---------|---------|-------------------