Unity 6.3
0 онлайн 78 гостей 3 в системе
Вход
Программирование в Unity Шаг 160 из 210

Асинхронные тесты

Вы можете использовать .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:

Окно Test Runner и консоль, отображающие выход предыдущего примера асинхронного тестового кода.
Окно 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."); }
  );
}

Дополнительные ресурсы