Node.js BigQuery unit testing

Viewed 771

I'm new to writing mocha unit tests and would like to ask how the unit test for the following code would look like (especially for the BigQuery part). The code is in a PubSub triggered Cloud Function and inserts rows into a BigQuery table:

/**
 * Triggered from a message on a Cloud Pub/Sub topic.
 *
 * @param {!Object} event Event payload.
 * @param {!Object} context Metadata for the event.
 */
const {BigQuery} = require('@google-cloud/bigquery');
const bigqueryClient = new BigQuery();

const dataset = 'dataset_name';
const table = 'table_name';

exports.sendtobigquery = (event, context) => {
  const pubsubMessage = Buffer.from(event.data, 'base64').toString();
  BigQueryInsert(pubsubMessage, dataset, table);
};

async function BigQueryInsert(pubsubMessage, dataset, table) {
  const date = new Date().toISOString();                // new date
  const rows = [{field1: pubsubMessage, field2: date}]; // new field2
  await bigqueryClient
    .dataset(dataset)
    .table(table)
    .insert(rows);
}

I've seen here Mocking ES6 BigQuery class that sinon and proxyquire should be used but I don't understand how to do that for my code exactly.

Any help is appreciated.

Update: I added a new date field as part of the row but I'm having trouble here too when it comes to the unit test. For the date I tried using sinon.useFakeTimers in the index.test.js from the answer like that

describe('66267929', () => {
  const now = new Date();
  const date = sinon.useFakeTimers(now.getTime());
  beforeEach(() => {
    sinon.restore();
  });
  afterEach(() => {
    sinon.restore();
  });
  it('should pass', async () => {
    ...  // same code
    ...  // same code
    sinon.assert.calledWithExactly(bigqueryClientStub.insert, [{ field1: 'teresa teng', field2: date }]);
  });
});

but that resulted in "AssertError: expected stub to be called once but was called 0 times". How can this be done?

1 Answers

Since you call the BigQueryInsert function without async/await, we need to flush the promise queue to ensure that all asynchronous method calls have been completed on the bigqueryClient object.

We use proxyquire and sinonjs to stub the BigQuery constructor.

We use returnsThis() to achieve the chain methods call.

E.g.

index.js:

const { BigQuery } = require('@google-cloud/bigquery');
const bigqueryClient = new BigQuery();

const dataset = 'dataset_name';
const table = 'table_name';

exports.sendtobigquery = (event, context) => {
  const pubsubMessage = Buffer.from(event.data, 'base64').toString();
  BigQueryInsert(pubsubMessage, dataset, table);
};

async function BigQueryInsert(pubsubMessage, dataset, table) {
  const rows = [{ field1: pubsubMessage }];
  await bigqueryClient.dataset(dataset).table(table).insert(rows);
}

index.test.js:

const proxyquire = require('proxyquire');
const sinon = require('sinon');

const flushPromises = () => new Promise((resolve) => setImmediate(resolve));

describe('66267929', () => {
  afterEach(() => {
    sinon.restore();
  });
  it('should pass', async () => {
    const bigqueryClientStub = {
      dataset: sinon.stub().returnsThis(),
      table: sinon.stub().returnsThis(),
      insert: sinon.stub().resolves(),
    };
    const googleCloundBigqueryStub = {
      BigQuery: sinon.stub().returns(bigqueryClientStub),
    };
    const { sendtobigquery } = proxyquire('./', {
      '@google-cloud/bigquery': googleCloundBigqueryStub,
    });
    const data = Buffer.from('teresa teng').toString('base64');
    sendtobigquery({ data });
    await flushPromises();
    sinon.assert.calledOnce(googleCloundBigqueryStub.BigQuery);
    sinon.assert.calledWithExactly(bigqueryClientStub.dataset, 'dataset_name');
    sinon.assert.calledWithExactly(bigqueryClientStub.table, 'table_name');
    sinon.assert.calledWithExactly(bigqueryClientStub.insert, [{ field1: 'teresa teng' }]);
  });
});

unit test result:

  66267929
    ✓ should pass (343ms)


  1 passing (346ms)

----------|---------|----------|---------|---------|-------------------
File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
----------|---------|----------|---------|---------|-------------------
All files |     100 |      100 |     100 |     100 |                   
 index.js |     100 |      100 |     100 |     100 |                   
----------|---------|----------|---------|---------|-------------------
Related