javascript - 如何在 javascript 中实现基本单元测试以实现 azure 持久功能编排
问题描述
什么是单元测试,它将在下面的编排器中伪造对 callActivity 的调用以返回已知值并期望编排器返回该值。
用于单元测试的 azure 持久函数文档[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
解决方案
我们采用的方法是将生成器方法提取到它自己的模块中。
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
});
});
正如所料,这是一个协调器的简单示例。我们有编排器,其中包含更多逻辑,并且测试将具有更多价值。
此外,我个人不会在第二次测试中使用模拟方法,而只会依靠使用存根测试输出来伪造依赖交互。
推荐阅读
- activemq - activemq 列表没有列出所有正在运行的代理
- api - 空手道 API 测试 @RunWith(Karate.class) 错误
- node.js - 如何避免 Node express API 多次触发?
- leveldb - 为什么rocksDB需要多个级别?
- azure-data-factory - 报告中的本地和云合并数据
- php - 占位符文本的 Laravel 语言翻译错误
- python - 在 Python 中寻找片状测试的根本原因
- r - 使用 R 从文件名中提取样本名称
- opencv - 我们可以在 proteus 8 的源代码页面中导入 dlib 吗?如果是怎么办?
- opc-ua - 使用 UA-.NETStandard 访问已知类型中的节点的最简单方法是什么?