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.
Os testes de unidade verificam a lógica de negócios e protegem contra regressões. Orquestrações duráveis coordenam várias atividades e podem ficar complexas rapidamente. Adicionar testes de unidade ajuda você a detectar erros antecipadamente.
Com o 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.
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 usando um cliente de teste e afirme sobre os resultados. Simulação necessário para C# e JavaScript. Python usa uma abordagem baseada em executor com eventos de histórico fictícios.
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
Microsoft.DurableTask.InProcessTestHostNuGet
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.
Simule o contexto de orquestração para controlar os valores de retorno de 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:
[Fact]
public async Task HelloCities_ReturnsExpectedGreetings()
{
var contextMock = new Mock<TaskOrchestrationContext>();
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. Não são necessários serviços externos ou processos sidecar.
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 para teste porque não têm 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. Elas 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);
}