2

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

Lin Du
  • 88,126
  • 95
  • 281
  • 483
OfirYaron
  • 157
  • 1
  • 9

1 Answers1

3

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

Lin Du
  • 88,126
  • 95
  • 281
  • 483