包含此页的版本:
不含此页的版本:
可以使用 .NET 任务异步编程模型编写异步测试。如果你不熟悉异步编程及其应用程序,请参阅 Microsoft 文档以获取综合指南。NUnit Test 的文档还解释了异步测试的要求。异步代码在主线程上运行,Unity 测试框架将await它通过检查任务是在 Play 模式的每次更新或 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
{
}
}
下面显示了在测试运行程序测试框架包(以前称为测试运行程序)是一个 Unity 工具,可在编辑模式和播放模式下测试代码,也可以在目标平台(如独立平台、Android 或 iOS)上测试代码。更多信息
请参阅术语表窗:
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."); }
);
}