Модульное тестирование надежной функции C# Azure с помощью внешних событий

#c# #unit-testing #azure-functions #xunit #azure-durable-functions

Вопрос:

У меня есть надежный оркестратор функций Azure, который ожидает двух внешних событий. Как только они оба были получены, оркестратор вызывает функцию действия.

Есть ли способ модульного тестирования этого оркестратора, чтобы убедиться, что функция activity вызывается только после получения обоих событий?

Вот код функции оркестратора:

 [FunctionName("MyOrchestrator")]

public static async Task MyOrchestrator(

    [OrchestrationTrigger] IDurableOrchestrationContext context)

{
    var event1 = context.WaitForExternalEvent<string>("Event1");
    var event2 = context.WaitForExternalEvent<string>("Event2");
    await Task.WhenAll(event1, event2);

    await context.CallActivityAsync<object>("Activity1", null);
    context.SetOutput(new { Status = "Complete" });
}
 

Комментарии:

1. Почему бы просто не поиздеваться IDurableOrchestrationContext ?

2. @StephenCleary Спасибо — я просто попробовал WaitForExternalEvent вернуться TaskCompletionSource . Затем я запускаю оркестратор в фоновой задаче, чтобы предотвратить блокировку модульного теста, затем я выполняю задачи событий, чтобы разблокировать оркестратор, и продолжаю вызывать действие. Затем я могу проверить CallActivityAsync , был ли получен вызов. Проблема в том, что существует задержка между выполнением задач события и вызываемым действием, поэтому мне нужно Thread.Sleep между ними, что кажется немного запутанным. Хотя на самом деле не могу придумать лучшего решения.

3. @ChrisFulstow Не используйте Thread.Sleep . вместо этого ждите Task.Delay с желаемой задержкой, а затем утверждайте ожидаемое поведение.

4. @Nkosi хороший момент, это всего лишь модульный тест, но все же лучше не блокировать поток. В идеале, хотя я бы хотел избавиться от необходимости в том или ином.

5. @ChrisFulstow на самом деле это не нужно, как только макет настроен правильно. Проверьте предоставленный ответ.

Ответ №1:

Если издевательский контекст организован правильно, то действие может произойти только после ожидания завершения двух задач внешнего события.

 //Arrange
var context = new Mock<IDurableOrchestrationContext>(); //MOQ

context.Setup(_ => _.WaitForExternalEvent<string>(It.IsAny<string>()))
    .Returns(async () => {
        await Task.Delay(10); //Delay is optional
        return "";
    });

context.Setup(_ => _.CallActivityAsync<object>(It.IsAny<string>(), It.IsAny<object>()))
    .ReturnsAsync(null);

//Act
await MyFunction.MyOrchestrator(context.Object);

//Assert
context.Verify(_ => _.WaitForExternalEvent<string>("Event1"));
context.Verify(_ => _.WaitForExternalEvent<string>("Event2"));
context.Verify(_ => _.CallActivityAsync<object>("Activity1", null));
context.Verify(_ => _.SetOutput(It.IsAny<object>()));
 

В приведенном выше примере с использованием MOQ события обычно настраиваются для задержки перед выполнением задачи. Только тогда будет вызвана активность.

Затем тест проверяет конкретные вызовы, чтобы подтвердить ожидаемое поведение.

Что касается того, насколько полезен этот модульный тест, не очень много, так как он в основном тестирует, что код фреймворка ожидает, как и ожидалось.