Kommentar
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Enhetstestning av varaktiga orkestreringar hjälper dig att verifiera affärslogik och fånga fel tidigt. Orkestreringar samordnar flera aktiviteter och kan växa komplext snabbt, så tester skyddar mot regressioner när arbetsflödet utvecklas.
Välj den flik som matchar projektet: Durable Functions om du använder Azure Functions eller Durable Task SDK:er om du använder fristående SDK utan Azure Functions.
Med Durable Functions testar du orkestreringsfunktioner, aktiviteter och klientfunktioner (utlösare) genom att mocka ramverkstillhandahållna kontextobjekt och anropa dina funktioner direkt. Den här metoden isolerar din affärslogik från Azure Functions runtime.
Här är ett minimalt C#-orkestreringstest för att visa mönstret:
[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);
}
Resten av den här artikeln beskriver det här mönstret i detalj för C# och Python.
De fristående Durable Task SDK:erna tillhandahåller inbyggd testinfrastruktur som kör orkestreringar i minnet utan externa beroenden. Du registrerar orkestratorer och aktiviteter med en testarbetare, schemalägger orkestreringar via en testklient och bekräftar resultatet. Inget hån krävs för C# och JavaScript. Python använder en generatorbaserad metod med manuell resultatinmatning.
Här är ett minimalt C#-test för att visa mönstret:
[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);
}
Resten av den här artikeln beskriver det här mönstret i detalj för C#, Python och JavaScript.
Förutsättningar
- xUnit – testramverk
- Moq – testverktyg för simulering
- Kunskaper om .NET isolerad arbetsmodell
- xUnit – testramverk
- NuGet-paketet
Microsoft.DurableTask.InProcessTestHost(v1.0.0 eller senare)
Testa orkestratorfunktioner
Orchestrator-funktioner samordnar aktiviteter, timers och externa händelser. De innehåller vanligtvis mest affärslogik och drar mest nytta av enhetstestning.
Håna orkestreringskontexten för att kontrollera returvärdena för aktivitetsanrop. Anropa sedan orkestratorn direkt och verifiera utdata.
Tänk på den här orkestratorn som anropar en aktivitet tre gånger:
[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;
}
Använd Moq för att mocka TaskOrchestrationContext och konfigurera förväntade returvärden för varje aktivitetsanrop.
Note
Mönstret It.Is<TaskName>(...) krävs eftersom CallActivityAsync accepterar en TaskName struct, inte en vanlig sträng. Moq behöver den explicita typmatchningen.
[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]);
}
Använd DurableTaskTestHost för att köra orkestreringar i minnet. Registrera dina produktionsorkestrator- och aktivitetsklasser, schemalägg en orkestrering och verifiera resultatet.
Med tanke på dessa produktionsklasser:
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}!");
}
}
Registrera dessa objekt direkt i testmiljön:
[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 kör en komplett orkestreringsmotor i minnet. Inga externa tjänster eller sidoprocesser krävs.
Testa aktivitetsfunktioner
Aktivitetsfunktioner innehåller det faktiska arbetet – anropa API:er, bearbeta data eller interagera med externa system. De är den enklaste funktionstypen att testa eftersom de inte har något ramverksspecifikt uppspelningsbeteende.
Aktivitetsfunktioner i Azure Functions tar emot indata och eventuellt en FunctionContext. Testa dem som andra funktioner:
[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);
}
Aktivitetsfunktioner tar emot ett kontextobjekt och indata. Kontexten innehåller metadata som orkestrerings-ID och uppgifts-ID, men de flesta tester behöver det inte.
Med hjälp av SayHelloActivity klassen från orchestrator-exemplet anropar du RunAsync direkt med en falsk kontext:
[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);
}
När du använder DurableTaskTestHostkörs aktiviteter också som en del av orkestreringstestet. Du behöver inte separata aktivitetstester om inte aktiviteten har komplex logik.
Testa klientfunktioner
Klientfunktioner (kallas även utlösarfunktioner) startar orkestreringar och hanterar instanser. De använder den hållbara klientbindningen för att interagera med orkestreringsmotorn.
Tänk på den här HTTP-utlösaren som startar en orkestrering:
[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);
}
Mocka DurableTaskClient för att returnera ett känt instans-ID.
[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);
}
Testa klientåtgärder
Med fristående Durable Task SDK:er använder klientåtgärder (schemaläggning av orkestreringar, frågestatus, trigga händelser) samma TestOrchestrationClient som redan visas i orchestrator-testerna. Det finns ingen separat klientfunktion – du anropar klient-API:et direkt.
DurableTaskTestHost visar host.Client, som är en fullt fungerande DurableTaskClient. Använd den för att testa åtgärder på klientnivå såsom schemaläggning, frågehantering eller att avsluta orkestreringar.
[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);
}