You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Durable Function编排器函数部分模拟测试技术咨询

问题

我希望测试Durable Function中编排器函数的行为,仅对依赖外部源的调用进行模拟,以此验证编排器函数的步骤组合逻辑是否正常工作。我无需对每个新增的活动触发器单独测试,目标是验证最终结果。现咨询以下问题:

  1. 这种测试方式是否可行?
  2. 能否在测试设置中获取一个活动触发器的执行结果,作为另一个活动触发器设置的输入?

当前未完成的测试方法

[Fact]
public async Task TestOrchestrator()
{
    // Setup
    var fileContent = Encoding.UTF8.GetBytes("this is some filecontent 😀");
    var encodedFileContent = Encoding.Latin1.GetString(fileContent);
    const string runId = "changeme";

    var loggerMock = new Mock<ILogger>();
    var clientMock = new Mock<IDurableOrchestrationContext>();

    clientMock
        .Setup(context => context.CallActivityAsync<byte[]>(nameof(GetBlobContentAsByteArray), runId))
        .ReturnsAsync(fileContent);

    // I want EncodeToISO88591 to do it's thing instead of me having to return a fake value.
    // clientMock
    //     .Setup(context => context.CallActivityAsync<string>(nameof(EncodeToISO88591), new DataObject<byte[]>(fileContent)))
    //     .ReturnsAsync(encodedFileContent);

    // I want this to consume the value of EncodeToISO88591, not a fake one.
    clientMock
        .Setup(context => context.CallActivityAsync<bool>(nameof(UploadToSftp), encodedFileContent))
        .ReturnsAsync(true);

    //Act

    await RunOrchestrator(clientMock.Object, loggerMock.Object);

}

编排器函数代码

public record DataObject<T>(T Data);

const string OrchestratorFunctionName = nameof(RunOrchestrator);
const string EncodeFunctionName = nameof(EncodeToISO88591);
const string GetBlobContentFunctionName = nameof(GetBlobContentAsByteArray);
const string UploadToSftpFunctionName = nameof(UploadToSftp);


[FunctionName(OrchestratorFunctionName)]
public static async Task RunOrchestrator(
    [OrchestrationTrigger] IDurableOrchestrationContext context, ILogger logger)
{
    if (!context.IsReplaying)
        logger.LogInformation("Starting Orchestration");

    var runId = context.GetInput<string>();

    byte[] blobContent = await context.CallActivityAsync<byte[]>(GetBlobContentFunctionName, runId);
    string encodedContent = await context.CallActivityAsync<string>(EncodeFunctionName, new DataObject<byte[]>(blobContent));
    bool sftpUploadSuccess = await context.CallActivityAsync<bool>(UploadToSftpFunctionName, encodedContent);

    if (!sftpUploadSuccess)
        logger.LogError("Failed to upload to sftp");

    if (!context.IsReplaying)
        logger.LogInformation($"Completed Orchestration of instance {context.InstanceId}");
}
回答
  1. 这种测试方式完全可行
    这种聚焦编排器流程逻辑、仅模拟外部依赖的思路是Durable Function测试的最佳实践之一。只需Mock依赖外部资源(如Blob存储、SFTP服务器)的活动函数,让纯逻辑处理的活动函数(比如EncodeToISO88591)真实执行,既能验证编排器的步骤顺序、数据传递逻辑,又避免了外部依赖带来的不稳定,完全符合你的测试目标。

  2. 可以实现让一个活动的真实执行结果作为另一个活动的输入
    不需要预先MockEncodeToISO88591的返回值,而是在MockCallActivityAsync时,匹配到该活动调用后直接执行真实逻辑,再将结果传递给后续的UploadToSftp调用。修改后的测试代码如下:

[Fact]
public async Task TestOrchestrator()
{
    // Setup
    var fileContent = Encoding.UTF8.GetBytes("this is some filecontent 😀");
    const string runId = "changeme";

    var loggerMock = new Mock<ILogger>();
    var clientMock = new Mock<IDurableOrchestrationContext>();

    // 模拟依赖外部的GetBlobContentAsByteArray活动
    clientMock
        .Setup(context => context.CallActivityAsync<byte[]>(nameof(GetBlobContentAsByteArray), runId))
        .ReturnsAsync(fileContent);

    // 让EncodeToISO88591真实执行,返回计算后的结果
    clientMock
        .Setup(context => context.CallActivityAsync<string>(nameof(EncodeToISO88591), It.IsAny<DataObject<byte[]>>()))
        .ReturnsAsync((DataObject<byte[]> input) => 
        {
            // 直接调用真实的编码逻辑
            return Encoding.Latin1.GetString(input.Data);
        });

    // 捕获EncodeToISO88591的返回值,用于后续验证
    string actualEncodedContent = null;
    clientMock
        .Setup(context => context.CallActivityAsync<bool>(nameof(UploadToSftp), It.IsAny<string>()))
        .Callback<string>(content => actualEncodedContent = content)
        .ReturnsAsync(true);

    // Act
    await RunOrchestrator(clientMock.Object, loggerMock.Object);

    // Assert:验证传递给UploadToSftp的内容是正确的编码结果
    var expectedEncodedContent = Encoding.Latin1.GetString(fileContent);
    Assert.Equal(expectedEncodedContent, actualEncodedContent);
}

关键说明:

  • 对EncodeToISO88591使用It.IsAny<DataObject<byte[]>>()匹配任意输入,在ReturnsAsync中直接执行真实编码逻辑并返回结果。
  • 对UploadToSftp用Callback<string>捕获实际传入的参数,通过断言验证该参数是否符合预期,确保编排器正确传递了前一个活动的结果。
  • 这种方式既隔离了外部依赖,又保留了纯逻辑活动的真实执行,完美验证编排器的流程和数据传递逻辑。

内容的提问来源于stack exchange,提问作者Marcus Nätteldal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 17:01:34