#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 события обычно настраиваются для задержки перед выполнением задачи. Только тогда будет вызвана активность.
Затем тест проверяет конкретные вызовы, чтобы подтвердить ожидаемое поведение.
Что касается того, насколько полезен этот модульный тест, не очень много, так как он в основном тестирует, что код фреймворка ожидает, как и ожидалось.