Асинхронные тесты
Вы можете использовать .NET Модель асинхронного программирования задач для написания асинхронных тестов. Если вы новичок в асинхронном программировании и его приложениях, обратитесь к Документация Microsoft для всеобъемлющего руководства. Документация для NUnit Тест, также объясняет требования к асинхронному тесту. Асинхронный код запускается на главном потоке и Unity Испытательная основа будет await проверяя, выполняется ли задача на каждом из обновление для режима Play или на каждом EditorApplication.update вне режима игры.
Обратите внимание, что любые сообщения журнала с ошибками будут оцениваться сначала после завершения асинхронного теста. Это означает, что если в асинхронном тесте имеется сообщение журнала с ошибками, оно не будет отражено в отчете до завершения теста.
Следующий фрагмент кода демонстрирует асинхронный тест, основанный на примере Microsoft по приготовлению завтрака. Обратите внимание, что метод тестирования помечен ключевым словом async и имеет тип возврата Task. Мы создаем список задач, соответствующих асинхронным методам, представляющим части процесса приготовления завтрака. Мы используем await для запуска этих задач в неблокирующем режиме, записываем в журнал, когда каждая из них завершена, и снова записываем в журнал, когда все завершены.
using System.Collections.Generic;
using System.Threading.Tasks;
using NUnit.Framework;
using UnityEngine;
public class AsyncExample
{
[Test]
public async Task MakeBreakfast_InTheMorning_IsEdible()
{
var eggsTask = FryEggsAsync(2);
var baconTask = FryBaconAsync(3);
var toastTask = MakeToastWithButterAndJamAsync(2);
var breakfastTasks = new List<Task> { eggsTask, baconTask, toastTask };
while (breakfastTasks.Count > 0)
{
Task finishedTask = await Task.WhenAny(breakfastTasks);
if (finishedTask == eggsTask)
{
Debug.Log("eggs are ready");
}
else if (finishedTask == baconTask)
{
Debug.Log("bacon is ready");
}
else if (finishedTask == toastTask)
{
Debug.Log("toast is ready");
}
breakfastTasks.Remove(finishedTask);
}
Debug.Log("Breakfast is ready!");
}
static async Task<Toast> MakeToastWithButterAndJamAsync(int number)
{
var toast = await ToastBreadAsync(number);
ApplyButter(toast);
ApplyJam(toast);
return toast;
}
private static Juice PourOJ()
{
Debug.Log("Pouring orange juice");
return new Juice();
}
private static void ApplyJam(Toast toast) =>
Debug.Log("Putting jam on the toast");
private static void ApplyButter(Toast toast) =>
Debug.Log("Putting butter on the toast");
private static async Task<Toast> ToastBreadAsync(int slices)
{
for (int slice = 0; slice < slices; slice++)
{
Debug.Log("Putting a slice of bread in the toaster");
}
Debug.Log("Start toasting...");
await Task.Delay(3000);
Debug.Log("Remove toast from toaster");
return new Toast();
}
private static async Task<Bacon> FryBaconAsync(int slices)
{
Debug.Log($"putting {slices} slices of bacon in the pan");
Debug.Log("cooking first side of bacon...");
await Task.Delay(3000);
for (int slice = 0; slice < slices; slice++)
{
Debug.Log("flipping a slice of bacon");
}
Debug.Log("cooking the second side of bacon...");
await Task.Delay(3000);
Debug.Log("Put bacon on plate");
return new Bacon();
}
private static async Task<Egg> FryEggsAsync(int howMany)
{
Debug.Log("Warming the egg pan...");
await Task.Delay(3000);
Debug.Log($"cracking {howMany} eggs");
Debug.Log("cooking the eggs ...");
await Task.Delay(3000);
Debug.Log("Put eggs on plate");
return new Egg();
}
private static Coffee PourCoffee()
{
Debug.Log("Pouring coffee");
return new Coffee();
}
public struct Toast
{
}
public struct Juice
{
}
public struct Bacon
{
}
public struct Egg
{
}
public struct Coffee
{
}
}
Ниже показан результат запуска этого примера в окне Test Runner:
Редактор зависает на Assert.ThrowsAsync и обходный путь
Утверждение NUnit для асинхронного кода, Assert.ThrowsAsync, блокирует вызывающий поток до завершения асинхронной функции, которую вы передали. По умолчанию Unity запускает асинхронные функции на главном потоке в случае, если им нужно вызвать Редактор API, что означает, что Assert.ThrowsAsync может заблокировать главный поток и вызвать замораживание Редактора.
Чтобы обойти эту проблему, вы можете развернуть логику Assert.ThrowsAsync в свои собственные блоки try/catch и утверждать, что вы что-то поймали. Например, делает следующее:
[Test]
public async Task ThisDoesNotLockTheMainThread()
{
bool caught = false;
try
{
await Task.Delay(1000); throw new System.Exception("Hello world."); }
catch (System.Exception x)
{
caught = true;
}
Assert.That(caught);
}
Вместо это:
[Test]
public void ThisLocksTheMainThread()
{
Assert.ThrowsAsync<System.Exception>(async () =>
{ await Task.Delay(1000); throw new System.Exception("Hello world."); }
);
}