Search code examples
node.jsunit-testinggoogle-bigquerymocha.jssinon

Mocking ES6 BigQuery class


I have a small class which wraps a basic big query operation:

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


export default class BQ {
    bigquery;

    constructor(projectId, keyFilename) {
        const options = {
            projectId,
            keyFilename,
            autoRetry: true,
        };
        
        this.bigquery = new BigQuery(options);
    }

    async query(query) {
        const options = {
            query,
            location: 'us',
        };
        const [job] = await this.bigquery.createQueryJob(options);
        const [rows] = await job.getQueryResults();
        return rows;
    }
}

I am trying to write a mocha unit test for the query method. However, I keep getting stuck with creating mocks in js. There are many options with Sinon, sandboxes, stubs, etc. I would think that I need to stub an instance attribute like,

const bq = new BQ(projectId, keyFilename);
const bqStub = sandbox.stub(bq, 'bigquery');

But there are methods that keep trying to actually auth to google that I need stubbed too. Any help on how to get started would be wonderful.


Solution

  • You can use Link Seams with CommonJS, we need proxyquire to construct our seams.

    E.g.

    bq.js:

    const { BigQuery } = require('@google-cloud/bigquery');
    
    export default class BQ {
      bigquery;
    
      constructor(projectId, keyFilename) {
        const options = {
          projectId,
          keyFilename,
          autoRetry: true,
        };
    
        this.bigquery = new BigQuery(options);
      }
    
      async query(query) {
        const options = {
          query,
          location: 'us',
        };
        const [job] = await this.bigquery.createQueryJob(options);
        const [rows] = await job.getQueryResults();
        return rows;
      }
    }
    

    bq.test.js:

    import proxyquire from 'proxyquire';
    import sinon from 'sinon';
    
    describe('62492844', () => {
      it('should pass', async () => {
        const rows = [{ id: 1 }, { id: 2 }];
        const job = {
          getQueryResults: sinon.stub().returns([rows]),
        };
        const bigquery = {
          createQueryJob: sinon.stub().returns([job]),
        };
        const BigQueryStub = sinon.stub().returns(bigquery);
        const BQ = proxyquire('./bq', {
          '@google-cloud/bigquery': { BigQuery: BigQueryStub },
        }).default;
    
        const bq = new BQ('projectId', './svc.json');
        sinon.assert.calledWithExactly(BigQueryStub, {
          projectId: 'projectId',
          keyFilename: './svc.json',
          autoRetry: true,
        });
        const actual = await bq.query('query');
        sinon.assert.calledWithExactly(bigquery.createQueryJob, { query: 'query', location: 'us' });
        sinon.assert.calledOnce(job.getQueryResults);
        sinon.assert.match(actual, rows);
      });
    });
    

    unit test result:

      62492844
        ✓ should pass (2427ms)
    
    
      1 passing (2s)
    
    ----------|---------|----------|---------|---------|-------------------
    File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
    ----------|---------|----------|---------|---------|-------------------
    All files |     100 |      100 |     100 |     100 |                   
     bq.ts    |     100 |      100 |     100 |     100 |                   
    ----------|---------|----------|---------|---------|-------------------