javascriptunit-testingjestjsnode-pg-pool

Using Jest to test anonymous function inside 3rd party module


I have a pretty simple module that use pg (node-postgre lib) module, I'd like to implement a Jest test and while mocking the pg module I would like to run it's callback function to see the console.log runs and my callback is being invoked

I have mocked the module and tried to spy and replace the 'query' method but it failed and crushed, any Idea what am I doing wrong?

Test Subject:

import {Pool} from 'pg';

const pool = new Pool();

module.exports = {
  query: (text, params, callback) => {
    const start = Date.now();

    return pool.query(text, params, (err, res) => {
      const duration = Date.now() - start;
      console.log('executed query', {text, duration, rows: res.rowCount});
      callback(err, res);
    });
  }
};

Test:

jest.mock('pg');

import module from './index';
import { Pool } from 'pg'

beforeAll(() => {
  Pool.mockImplementation(()=>{return jest.fn()});
});


  it('callback is called', () => {
    const cb = (err, res) => true;
    const query = jest.spyOn(Pool, "query");         // <---- Not right, Error
    query.mockImplementation((a,b,c) => c({},{}));
    const resolve = module.query('QUERY TEXT', { a: 1, b: 2}, cb);
    resolve();                                       // <---- Not what I expect
    expect(cb).toBeCalled();
  });
});

Error thrown: Error: Cannot spy the query property because it is not a function; undefined given instead

  20 |   it('callback is called', () => {
  21 |     const cb = (err, res) => true;
> 22 |     const query = jest.spyOn(Pool, "query");
     |                        ^
  23 |     query.mockImplementation((a,b,c) => c({},{}));
  24 |     const resolve = module.query('QUERY TEXT', { a: 1, b: 2}, cb);
  25 |     resolve();

  at ModuleMockerClass.spyOn (node_modules/jest-mock/build/index.js:697:15)
  at Object.spyOn (src/db/index.test.js:22:24)

Thanks


Solution

  • Here is the unit test solution:

    index.js:

    import { Pool } from 'pg';
    const pool = new Pool();
    
    module.exports = {
      query: (text, params, callback) => {
        const start = Date.now();
    
        return pool.query(text, params, (err, res) => {
          const duration = Date.now() - start;
          console.log('executed query', { text, duration, rows: res.rowCount });
          callback(err, res);
        });
      }
    };
    

    index.spec.js:

    import mod from '.';
    import { Pool } from 'pg';
    
    jest.mock('pg', () => {
      const mPool = {
        query: jest.fn()
      };
      return { Pool: jest.fn(() => mPool) };
    });
    
    const pool = new Pool();
    
    afterEach(() => {
      jest.resetAllMocks();
      jest.restoreAllMocks();
    });
    
    it('callback is called', done => {
      let queryCallback;
      pool.query.mockImplementation((text, params, callback) => {
        queryCallback = callback;
      });
      const logSpy = jest.spyOn(console, 'log');
      const userCallback = jest.fn();
      mod.query('text', 'params', userCallback);
      const mRes = { rowCount: 1 };
      queryCallback(null, mRes);
      expect(pool.query).toBeCalledWith('text', 'params', queryCallback);
      expect(userCallback).toBeCalledWith(null, mRes);
      expect(logSpy).toBeCalledWith('executed query', { text: 'text', duration: expect.any(Number), rows: 1 });
      done();
    });
    

    Unit test result with 100% coverage:

     PASS  src/stackoverflow/52831401/index.spec.js
      ✓ callback is called (15ms)
    
      console.log node_modules/jest-mock/build/index.js:860
        executed query { text: 'text', duration: 0, rows: 1 }
    
    ----------|----------|----------|----------|----------|-------------------|
    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:       1 passed, 1 total
    Snapshots:   0 total
    Time:        4.026s
    

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