【问题标题】:Node.js BigQuery unit testingNode.js BigQuery 单元测试
【发布时间】:2021-05-21 21:00:46
【问题描述】:

我是编写 mocha 单元测试的新手,想问一下以下代码的单元测试是什么样子的(尤其是 BigQuery 部分)。该代码位于 PubSub 触发的 Cloud Function 中,并将行插入到 BigQuery 表中:

/**
 * 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);
}

我在这里看到 Mocking ES6 BigQuery class 应该使用 sinon 和 proxyquire,但我不明白如何为我的代码准确地做到这一点。

感谢任何帮助。

更新: 我添加了一个新的日期字段作为该行的一部分,但是在单元测试方面我也遇到了麻烦。对于我尝试在index.test.js 中使用sinon.useFakeTimers 的日期,来自这样的答案

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 }]);
  });
});

但这导致“AssertError:预期存根被调用一次但被调用 0 次”。如何做到这一点?

【问题讨论】:

    标签: node.js unit-testing google-bigquery sinon


    【解决方案1】:

    由于您在没有async/await 的情况下调用BigQueryInsert 函数,我们需要刷新承诺队列以确保所有异步方法调用都已在bigqueryClient 对象上完成。

    我们使用proxyquire 和 sinonjs 来存根BigQuery 构造函数。

    我们使用returnsThis()来实现链式方法调用。

    例如

    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' }]);
      });
    });
    

    单元测试结果:

      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 |                   
    ----------|---------|----------|---------|---------|-------------------
    

    【讨论】:

    • 感谢您的快速答复。它有很大帮助。还有一个问题:我在行和一个 catch 块中添加了另一个带有 new Date().toISOString() 的字段。我不太确定您提供的单元测试必须如何扩展。我编辑了我的问题,指定了几个添加的代码行和我尝试过的内容。你知道这是怎么做到的吗?
    猜你喜欢
    • 2019-05-11
    • 1970-01-01
    • 1970-01-01
    • 2011-11-07
    • 1970-01-01
    • 2013-03-08
    • 1970-01-01
    • 1970-01-01
    • 2018-11-05
    相关资源
    最近更新 更多