Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
O teste de unidade de orquestrações duráveis ajuda você a verificar a lógica de negócios e detectar erros antecipadamente. As orquestrações coordenam várias atividades e podem ficar complexas rapidamente, portanto, os testes protegem contra regressões à medida que o fluxo de trabalho evolui.
Selecione a guia que corresponde ao seu projeto: Durable Functions se você usar Azure Functions, ou SDKs de Tarefas Duráveis se utilizar o SDK autônomo sem Azure Functions.
Com Durable Functions, você testa orquestradores, atividades e funções de cliente (gatilho) simulando os objetos de contexto fornecidos pela estrutura e chamando suas funções diretamente. Essa abordagem isola sua lógica de negócios do runtime do Azure Functions.
Aqui está um teste mínimo do orquestrador em C# para mostrar o padrão:
[Fact]
public async Task MyOrchestrator_CallsActivity()
{
var contextMock = new Mock<TaskOrchestrationContext>();
contextMock.Setup(x => x.CallActivityAsync<string>(
It.IsAny<TaskName>(), It.IsAny<string>(), It.IsAny<TaskOptions>()))
.ReturnsAsync("result");
var result = await MyOrchestrator.Run(contextMock.Object);
Assert.Equal("result", result);
}
O restante deste artigo aborda esse padrão em detalhes para C# e Python.
Os SDKs de Tarefa Duráveis autônomos fornecem uma infraestrutura de teste interna que executa orquestrações na memória sem dependências externas. Registre orquestradores e atividades com um trabalho de teste, agende orquestrações por meio de um cliente de teste e afirme sobre os resultados. Nenhum mock é necessário para C# e JavaScript. Python usa uma abordagem baseada em gerador com injeção de resultado manual.
Aqui está um teste de C# mínimo para mostrar o padrão:
[Fact]
public async Task MyOrchestrator_Completes()
{
await using var host = await DurableTaskTestHost.StartAsync(tasks =>
{
tasks.AddOrchestrator<MyOrchestrator>();
tasks.AddActivity<MyActivity>();
});
string id = await host.Client.ScheduleNewOrchestrationInstanceAsync(nameof(MyOrchestrator));
var result = await host.Client.WaitForInstanceCompletionAsync(id, getInputsAndOutputs: true);
Assert.Equal(OrchestrationRuntimeStatus.Completed, result.RuntimeStatus);
}
O restante deste artigo aborda esse padrão em detalhes para C#, Python e JavaScript.
Pré-requisitos
- xUnit — estrutura de teste
- Moq - estrutura de simulação
- Familiaridade com o modelo de trabalho isolado .NET
- xUnit — estrutura de teste
- O pacote NuGet
Microsoft.DurableTask.InProcessTestHost(v1.0.0 ou posterior)
Testar funções de um orquestrador
As funções de orquestrador coordenam atividades, temporizadores e eventos externos. Normalmente, elas contêm mais lógica de negócios e se beneficiam mais do teste de unidade.
Simular o contexto de orquestração para controlar os valores retornados das chamadas de atividade. Em seguida, chame o orquestrador diretamente e verifique a saída.
Considere este orquestrador que chama uma atividade três vezes:
[Function(nameof(HelloCitiesOrchestration))]
public static async Task<List<string>> HelloCities(
[OrchestrationTrigger] TaskOrchestrationContext context)
{
var outputs = new List<string>
{
await context.CallActivityAsync<string>(nameof(SayHello), "Tokyo"),
await context.CallActivityAsync<string>(nameof(SayHello), "Seattle"),
await context.CallActivityAsync<string>(nameof(SayHello), "London")
};
return outputs;
}
Use o Moq para simular TaskOrchestrationContext e configurar valores retornados esperados para cada chamada de atividade.
Note
O It.Is<TaskName>(...) padrão é necessário porque CallActivityAsync aceita um TaskName struct, não uma cadeia de caracteres simples. Moq precisa da correspondência explícita de tipo.
[Fact]
public async Task HelloCities_ReturnsExpectedGreetings()
{
var contextMock = new Mock<TaskOrchestrationContext>();
// Mock each activity call to return a known value
contextMock.Setup(x => x.CallActivityAsync<string>(
It.Is<TaskName>(n => n.Name == nameof(SayHello)),
It.Is<string>(n => n == "Tokyo"),
It.IsAny<TaskOptions>())).ReturnsAsync("Hello Tokyo!");
contextMock.Setup(x => x.CallActivityAsync<string>(
It.Is<TaskName>(n => n.Name == nameof(SayHello)),
It.Is<string>(n => n == "Seattle"),
It.IsAny<TaskOptions>())).ReturnsAsync("Hello Seattle!");
contextMock.Setup(x => x.CallActivityAsync<string>(
It.Is<TaskName>(n => n.Name == nameof(SayHello)),
It.Is<string>(n => n == "London"),
It.IsAny<TaskOptions>())).ReturnsAsync("Hello London!");
var result = await HelloCitiesOrchestration.HelloCities(contextMock.Object);
Assert.Equal(3, result.Count);
Assert.Equal("Hello Tokyo!", result[0]);
Assert.Equal("Hello Seattle!", result[1]);
Assert.Equal("Hello London!", result[2]);
}
Use DurableTaskTestHost para executar orquestrações na memória. Registre seu orquestrador de produção e as classes de atividade, agende uma orquestração e verifique o resultado.
Considerando estas classes de produção:
class HelloCitiesOrchestrator : TaskOrchestrator<string, List<string>>
{
public override async Task<List<string>> RunAsync(
TaskOrchestrationContext context, string input)
{
var outputs = new List<string>
{
await context.CallActivityAsync<string>(nameof(SayHelloActivity), "Tokyo"),
await context.CallActivityAsync<string>(nameof(SayHelloActivity), "Seattle"),
await context.CallActivityAsync<string>(nameof(SayHelloActivity), "London")
};
return outputs;
}
}
class SayHelloActivity : TaskActivity<string, string>
{
public override Task<string> RunAsync(TaskActivityContext context, string name)
{
return Task.FromResult($"Hello {name}!");
}
}
Registre-os diretamente no host de teste:
[Fact]
public async Task HelloCities_ReturnsExpectedGreetings()
{
await using var host = await DurableTaskTestHost.StartAsync(tasks =>
{
tasks.AddOrchestrator<HelloCitiesOrchestrator>();
tasks.AddActivity<SayHelloActivity>();
});
string instanceId = await host.Client.ScheduleNewOrchestrationInstanceAsync(
nameof(HelloCitiesOrchestrator));
OrchestrationMetadata result = await host.Client.WaitForInstanceCompletionAsync(
instanceId, getInputsAndOutputs: true);
Assert.Equal(OrchestrationRuntimeStatus.Completed, result.RuntimeStatus);
var output = result.ReadOutputAs<List<string>>();
Assert.Equal(3, output.Count);
Assert.Equal("Hello Tokyo!", output[0]);
Assert.Equal("Hello Seattle!", output[1]);
Assert.Equal("Hello London!", output[2]);
}
DurableTaskTestHost executa um mecanismo de orquestração completo em memória. Nenhum serviço externo ou processos do tipo sidecar são necessários.
Funções de atividade de teste
As funções de atividade contêm o trabalho real : chamar APIs, processar dados ou interagir com sistemas externos. Eles são o tipo de função mais simples a ser testado porque não têm nenhum comportamento de reprodução específico da estrutura.
As funções de atividade em Azure Functions recebem uma entrada e, opcionalmente, um FunctionContext. Teste-os como qualquer outra função:
[Function(nameof(SayHello))]
public static string SayHello(
[ActivityTrigger] string name, FunctionContext executionContext)
{
return $"Hello {name}!";
}
[Fact]
public void SayHello_ReturnsExpectedGreeting()
{
var result = HelloCitiesOrchestration.SayHello("Tokyo", Mock.Of<FunctionContext>());
Assert.Equal("Hello Tokyo!", result);
}
As funções de atividade recebem um objeto de contexto e uma entrada. O contexto fornece metadados como a ID de orquestração e a ID da tarefa, mas a maioria dos testes não precisa dele.
Usando a SayHelloActivity classe do exemplo de orquestrador, chame RunAsync diretamente com um contexto fictício:
[Fact]
public async Task SayHello_ReturnsExpectedGreeting()
{
var activity = new SayHelloActivity();
var contextMock = new Mock<TaskActivityContext>();
var result = await activity.RunAsync(contextMock.Object, "Tokyo");
Assert.Equal("Hello Tokyo!", result);
}
Quando você usa DurableTaskTestHost, as atividades também são executadas como parte do teste de orquestração. Você não precisa de testes de atividade separados, a menos que a atividade tenha uma lógica complexa.
Testar funções de cliente
As funções de cliente (também chamadas de funções de gatilho) iniciam orquestrações e gerenciam instâncias. Eles usam a associação de cliente durável para interagir com o mecanismo de orquestração.
Considere este gatilho HTTP que inicia uma orquestração:
[Function("HelloCitiesOrchestration_HttpStart")]
public static async Task<HttpResponseData> HttpStart(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestData req,
[DurableClient] DurableTaskClient client,
FunctionContext executionContext)
{
string instanceId = await client.ScheduleNewOrchestrationInstanceAsync(
nameof(HelloCitiesOrchestration));
return await client.CreateCheckStatusResponseAsync(req, instanceId);
}
Simular DurableTaskClient para retornar uma ID de instância conhecida:
[Fact]
public async Task HttpStart_ReturnsAccepted()
{
var durableClientMock = new Mock<DurableTaskClient>("testClient");
var functionContextMock = new Mock<FunctionContext>();
var instanceId = "test-instance-id";
durableClientMock
.Setup(x => x.ScheduleNewOrchestrationInstanceAsync(
It.IsAny<TaskName>(),
It.IsAny<object>(),
It.IsAny<StartOrchestrationOptions>(),
It.IsAny<CancellationToken>()))
.ReturnsAsync(instanceId);
var mockRequest = CreateMockHttpRequest(functionContextMock.Object);
var responseMock = new Mock<HttpResponseData>(functionContextMock.Object);
responseMock.SetupGet(r => r.StatusCode).Returns(HttpStatusCode.Accepted);
durableClientMock
.Setup(x => x.CreateCheckStatusResponseAsync(
It.IsAny<HttpRequestData>(),
It.IsAny<string>(),
It.IsAny<CancellationToken>()))
.ReturnsAsync(responseMock.Object);
var result = await HelloCitiesOrchestration.HttpStart(
mockRequest, durableClientMock.Object, functionContextMock.Object);
Assert.Equal(HttpStatusCode.Accepted, result.StatusCode);
}
Testar operações do cliente
Com os SDKs de Tarefa Duráveis autônomos, as operações do cliente (agendando orquestrações, consultando status, gerando eventos) usam o mesmo TestOrchestrationClient já mostrado nos testes do orquestrador. Não existe nenhuma função de cliente separada – você chama a API do cliente diretamente.
DurableTaskTestHost expõe host.Client, que é um DurableTaskClient totalmente funcional. Use-o para testar operações no nível do cliente, como agendamento, consulta ou encerramento de orquestrações.
[Fact]
public async Task Client_CanQueryOrchestrationStatus()
{
await using var host = await DurableTaskTestHost.StartAsync(tasks =>
{
tasks.AddOrchestrator<HelloCitiesOrchestrator>();
tasks.AddActivity<SayHelloActivity>();
});
string instanceId = await host.Client.ScheduleNewOrchestrationInstanceAsync(
nameof(HelloCitiesOrchestrator));
// Query status while the orchestration runs
OrchestrationMetadata metadata = await host.Client.WaitForInstanceCompletionAsync(
instanceId, getInputsAndOutputs: true);
Assert.Equal(OrchestrationRuntimeStatus.Completed, metadata.RuntimeStatus);
Assert.Equal(instanceId, metadata.InstanceId);
}