4

I am trying to test a function which makes a connection to pg, using async/await,

import pg from 'pg';

module.exports.test = async (event, context, callback) => {
  const client = new pg.Client(someConnectionString);
  try {
    await client.connect();
  } catch (e) {
    return callback(e);
  }
  try {
    await client.query(await someAsyncFunction(test));
    client.end();
    return callback(null, 'success');
  } catch (e) {
    client.end();
    return callback(e);
  }
};

Don't really understand how I would mock this using jest? Any ideas?

Lin Du
  • 88,126
  • 95
  • 281
  • 483
John Smith
  • 103
  • 1
  • 4
  • 1
    what do you intend to test here? Most of the code involve interaction with pg library. If you want to unit test it, you need to stub/mock most of those calls, which is too complicated for the value that it brings. For the code that interacts with external systems like database, the most suitable type of tests in my opinion is integration tests (.i.e. spinning up an actual test db/in memory db, invoke the code above, make assertion based on state in db) – Phuong Nguyen Sep 06 '20 at 13:04
  • An `async function` should not take a `callback`. Instead, use the promise that it returns! – Bergi Sep 06 '20 at 15:52

2 Answers2

0

first of all, in jest you can return a promise to test async code:

import pg from 'pg';

module.exports.test = async (event, context) => {
  const client = new pg.Client(someConnectionString);
  try {
    await client.connect();
    await client.query(await someAsyncFunction(test));
  } finally {
    client.end();
  }
};

As any async function returns a promise you should not have to handle manually passing the error to a callback.

Now to the question about how to mock pg, you can create a fake pg object, but that's rarely ideal, I'll just show an example to answer the actual question.

You should try to setup a test db before running your tests, and then clean it up after, this way you don't have to mock anything and can use the real pg this would improve a lot the quality of the tests.

// mock example:
class MockClient {
  async connect() {
  }
  async query() {
  }
}
kigiri
  • 2,952
  • 21
  • 23
0

Here is the unit test solution:

index.js:

import pg from 'pg';
import { someAsyncFunction } from './someAsyncFunction';

const someConnectionString = 'someConnectionString';
const test = 'test';

module.exports = async (event, context, callback) => {
  const client = new pg.Client(someConnectionString);
  try {
    await client.connect();
  } catch (e) {
    return callback(e);
  }
  try {
    await client.query(await someAsyncFunction(test));
    client.end();
    return callback(null, 'success');
  } catch (e) {
    client.end();
    return callback(e);
  }
};

someAsyncFunction.js:

export async function someAsyncFunction() {}

index.test.js:

import pg from 'pg';
import fn from './';
import { someAsyncFunction } from './someAsyncFunction';

jest.mock('./someAsyncFunction', () => {
  return { someAsyncFunction: jest.fn() };
});
jest.mock('pg', () => {
  const mClient = { connect: jest.fn(), query: jest.fn(), end: jest.fn() };
  return { Client: jest.fn(() => mClient) };
});

describe('46152048', () => {
  afterEach(() => {
    jest.clearAllMocks();
  });
  afterAll(() => {
    jest.resetAllMocks();
  });
  it('should query success', async (done) => {
    someAsyncFunction.mockResolvedValueOnce('select 1;');
    const mClient = new pg.Client();
    mClient.connect.mockResolvedValueOnce();
    await fn({}, {}, (err, result) => {
      expect(pg.Client).toBeCalledWith('someConnectionString');
      expect(someAsyncFunction).toBeCalledWith('test');
      expect(mClient.query).toBeCalledWith('select 1;');
      expect(mClient.end).toBeCalledTimes(1);
      expect(err).toBeNull();
      expect(result).toBe('success');
      done();
    });
  });

  it('should handle error if connect database failed', async () => {
    const mError = new Error('network');
    const mClient = new pg.Client();
    mClient.connect.mockRejectedValueOnce(mError);
    await fn({}, {}, (err, result) => {
      expect(err.message).toBe('network');
      expect(result).toBeUndefined();
    });
  });

  it('should handle error if query failed', async () => {
    const mError = new Error('network');
    const mClient = new pg.Client();
    mClient.connect.mockResolvedValueOnce();
    mClient.query.mockRejectedValueOnce(mError);
    await fn({}, {}, (err, result) => {
      expect(err.message).toBe('network');
      expect(mClient.end).toBeCalledTimes(1);
      expect(result).toBeUndefined();
    });
  });
});

unit test result with coverage report:

 PASS  src/stackoverflow/46152048/index.test.js
  46152048
    ✓ should query success (11ms)
    ✓ should handle error if connect database failed (1ms)
    ✓ should handle error if query failed (1ms)

----------|----------|----------|----------|----------|-------------------|
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:       3 passed, 3 total
Snapshots:   0 total
Time:        4.103s, estimated 10s

source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/46152048

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