Search code examples
node.jspostgresqlunit-testingjestjssinon

How to Mock postgresql (pg) in node.js using jest


I am new in node.js. I am writing code in node.js for postgresql using pg and pg-native for serverless app. I need to write unit test for it. I am unable to mock pg client using jest or sinon.

My actual code is like this

const { Client } = require('pg');
export const getAlerts = async (event, context) => {

  const client = new Client({
    user: process.env.DB_USER,
    host: process.env.DB_HOST,
    database: process.env.DB_DATABASE,
    password: process.env.DB_PASSWORD,
    port: process.env.PORT
  });

  await client.connect();

  try {
    const result = await client.query(`SELECT * FROM public.alerts;`);
    console.log(result.rows);
    client.end();
    return success({ message: `${result.rowCount} item(s) returned`, data: result.rows, status: true });

  } catch (e) {
    console.error(e.stack);
    client.end();
    return failure({ message: e, status: false });
  }

};

How to mock pg client here?


Solution

  • Here is the unit test solution using jestjs:

    index.js:

    const { Client } = require('pg');
    const { success, failure } = require('./handler');
    
    export const getAlerts = async (event, context) => {
      const client = new Client({
        user: process.env.DB_USER,
        host: process.env.DB_HOST,
        database: process.env.DB_DATABASE,
        password: process.env.DB_PASSWORD,
        port: process.env.PORT,
      });
    
      await client.connect();
    
      try {
        const result = await client.query(`SELECT * FROM public.alerts;`);
        console.log(result.rows);
        client.end();
        return success({ message: `${result.rowCount} item(s) returned`, data: result.rows, status: true });
      } catch (e) {
        console.error(e.stack);
        client.end();
        return failure({ message: e, status: false });
      }
    };
    

    hander.js:

    export function success(data) {}
    export function failure(data) {}
    

    index.spec.js:

    import { getAlerts } from './';
    import { Client } from 'pg';
    import { success, failure } from './handler';
    
    jest.mock('pg', () => {
      const mClient = {
        connect: jest.fn(),
        query: jest.fn(),
        end: jest.fn(),
      };
      return { Client: jest.fn(() => mClient) };
    });
    
    jest.mock('./handler.js', () => {
      return {
        success: jest.fn(),
        failure: jest.fn(),
      };
    });
    
    describe('59540432', () => {
      let client;
      beforeEach(() => {
        client = new Client();
      });
      afterEach(() => {
        jest.clearAllMocks();
      });
      it('should success', async () => {
        client.query.mockResolvedValueOnce({ rows: [], rowCount: 0 });
        await getAlerts();
        expect(client.connect).toBeCalledTimes(1);
        expect(client.query).toBeCalledWith('SELECT * FROM public.alerts;');
        expect(client.end).toBeCalledTimes(1);
        expect(success).toBeCalledWith({ message: '0 item(s) returned', data: [], status: true });
      });
    
      it('should failure', async () => {
        const mError = new Error('dead lock');
        client.query.mockRejectedValueOnce(mError);
        await getAlerts();
        expect(client.connect).toBeCalledTimes(1);
        expect(client.query).toBeCalledWith('SELECT * FROM public.alerts;');
        expect(client.end).toBeCalledTimes(1);
        expect(failure).toBeCalledWith({ message: mError, status: false });
      });
    });
    

    Unit test result with 100% coverage:

     PASS  src/stackoverflow/59540432/index.spec.js (11.792s)
      59540432
        ✓ should success (16ms)
        ✓ should failure (5ms)
    
      console.log src/stackoverflow/59540432/index.js:3131
        []
    
      console.error src/stackoverflow/59540432/index.js:3155
        Error: dead lock
            at /Users/ldu020/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/59540432/index.spec.js:39:20
            at step (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/59540432/index.spec.js:33:23)
            at Object.next (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/59540432/index.spec.js:14:53)
            at /Users/ldu020/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/59540432/index.spec.js:8:71
            at new Promise (<anonymous>)
            at Object.<anonymous>.__awaiter (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/59540432/index.spec.js:4:12)
            at Object.<anonymous> (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/59540432/index.spec.js:38:24)
            at Object.asyncJestTest (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:102:37)
            at resolve (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-jasmine2/build/queueRunner.js:43:12)
            at new Promise (<anonymous>)
            at mapper (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-jasmine2/build/queueRunner.js:26:19)
            at promise.then (/Users/ldu020/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-jasmine2/build/queueRunner.js:73:41)
            at process._tickCallback (internal/process/next_tick.js:68:7)
    
    ----------|----------|----------|----------|----------|-------------------|
    File      |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
    ----------|----------|----------|----------|----------|-------------------|
    All files |      100 |      100 |      100 |      100 |                   |
     index.js |      100 |      100 |      100 |      100 |                   |
    ----------|----------|----------|----------|----------|-------------------|
    Test Suites: 1 passed, 1 total
    Tests:       2 passed, 2 total
    Snapshots:   0 total
    Time:        14.109s
    

    Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/59540432