Version: 6000.3
语言: 中文
参数化测试
运行测试

异步测试

可以使用 .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."); }
  );
}

其他资源

参数化测试
运行测试