如何在javascript中实现基本单元测试以实现天蓝色的持久功能编排

时间:2019-02-25 09:55:04

标签: javascript unit-testing azure-durable-functions

什么是单元测试,它将伪造下面的编排器中对callActivity的调用以返回已知值并期望编排器返回该值。

天蓝色的持久功能文档中用于单元测试的示例[1]都是用C#编写的,我无法复制 尽管有几次尝试,但它们仍使用javascript。这是因为我不知道如何使用伪造的上下文来构造协调器。

  const df = require('durable-functions');

  module.exports = df.orchestrator(function* orchestratorFunctionGenerator(context) {
    const input = context.df.getInput();
    const apimApiName = input.apimApiName;
    const indexNames = yield context.df.callActivity('GetIndexNames', apimApiName);
    const indexerName = indexNames.idle;
    const indexerStatus = yield context.df.callActivity('GetIndexerStatus', indexerName);
    return indexerStatus;
  });

[1] https://docs.microsoft.com/en-us/azure/azure-functions/durable/durable-functions-unit-testing

1 个答案:

答案 0 :(得分:0)

我们采用的方法是将generator方法提取到其自己的模块中。

module.exports = function* orchestratorFunctionGenerator(context) {
  const input = context.df.getInput();
  const apimApiName = input.apimApiName;
  const indexNames = yield context.df.callActivity('GetIndexNames', apimApiName);
  const indexerName = indexNames.idle;
  const indexerStatus = yield context.df.callActivity('GetIndexerStatus', indexerName);
  return indexerStatus;
};

然后要求

const df = require('durable-functions');
const generatorFunction = require('./generator-function');

module.exports = df.orchestrator(generatorFunction);

然后隔离测试功能

const chai = require('chai');
const sinon = require('sinon');
const getIndexerStatusOrchestratorGenerator = require('../../GetIndexerStatusOrchestrator/generator-function');

const expect = chai.expect;

function iterateGenerator(generator) {
  let result = generator.next();
  while (!result.done) {
    result = generator.next(result.value);
  }
  return result;
}

describe('getIndexerStatusOrchestrator', () => {
  it('happy path should return \'inProgress\'', () => {
    const indexNames = { active: 'index-1', idle: 'index-2' };
    const apimApiName = 'api';
    const input = { apimApiName };
    const stubCallActivity = sinon.stub();
    stubCallActivity.withArgs('GetIndexNames', apimApiName).returns(indexNames);
    stubCallActivity.withArgs('GetIndexerStatus', indexNames.idle).returns('inProgress');

    const context = {
      df: {
        callActivity: stubCallActivity,
        getInput: sinon.fake.returns(input),
      },
    };

    const generator = getIndexerStatusOrchestratorGenerator(context);

    const result = iterateGenerator(generator);
    expect(result.value).to.equal('inProgress');
  });
  it('indexer status should be for the idle index', () => {
    const indexNames = { active: 'index-1', idle: 'index-2' };
    const apimIndexName = 'api';
    const input = { apimApiName: apimIndexName };
    const stubCallActivity = sinon.stub();
    stubCallActivity.withArgs('GetIndexNames', apimIndexName).returns(indexNames);
    stubCallActivity.withArgs('GetIndexerStatus', indexNames.idle);
    // use stub as a mock since we need both stub and mock behaviour
    // for 'callActivity' and this was the easier option
    stubCallActivity.withArgs('GetIndexerStatus').callsFake((method, indexerName) => {
      expect.fail(`Unexpected indexer name ${indexerName}`);
    });

    const context = {
      df: {
        callActivity: stubCallActivity,
        getInput: sinon.fake.returns(input),
      },
    };

    const generator = getIndexerStatusOrchestratorGenerator(context);

    iterateGenerator(generator);

    // expectations set above
  });
});

可以预料,这是编排器的一个简单例子。我们的编排器中包含更多逻辑,并且测试将具有更大价值。

此外,我个人不会在第二次测试中使用模拟方法,而只是依靠使用存根测试输出来伪造依赖项交互。