【问题标题】:How to test Unity Coroutines in Unity Test Runner如何在 Unity Test Runner 中测试 Unity Coroutines
【发布时间】:2019-11-07 09:45:33
【问题描述】:

我目前正在使用 Unity 2019 和 C# 开发游戏,为此我使用了很多事件和协程。协程是必要的,因为在其中完成了许多计算,我不希望游戏在那段时间冻结。现在我开始使用 Test Runner(播放模式)编写单元测试,而 协程只是没有执行。由于我确实需要确保它们按预期工作,因此不可能仅在“更高级别”进行测试。

我已经尝试过正常的 StartCoroutine() 方法,但在测试文件中不起作用(“找不到方法名称”)。此外,我将我的一个协程重构为一个普通的 void 方法,一切运行良好,并且通过了测试。我使用 Visual Studio 调试器查看它是否会跳转到协程执行中,但它没有。所以问题很明显是协程没有被执行。我曾考虑将计算逻辑移至另一个 void 函数并测试该函数(并省略协程),但我必须确保在协程中完成的迭代也能正常工作(它们通常比示例中更复杂)。

这是一个显示一般结构的最小示例(通常完成更多计算)。

public class MeasuredValues
{
    List<Vector3> slidingWindow; // this is already filled when Normalize() is executed

    public IEnumerator Normalize()
    {
        //find coordinate system origin
        Vector3 originPosition = GetOrigin(); // returns a Vector3
        //normalization
        for (int i = 0; i < slidingWindow.Count; i++)
        {
            yield return null;
            //reset point to be at (0,y,0)
            slidingWindow[i] -= originPosition;
        }
    }
}

在我想要的测试文件中:

[Test]
public void TestNormalization()
{
    MeasuredValues myMeasuredValues = new MeasuredValues();
    // add many Vector3 to slidingWindow
    // call coroutine
    // assert, that the values are now as expected
}

我尝试了myMeasuredValues.Normalize()(没用,调试器刚刚跳过它)和StartCoroutine(myMeasuredValues.Normalize)(没用,StartCoroutine 在该上下文中不可用)。 最后,我尝试了

    while (test.Normalize().MoveNext())
    {
        yield return null;
    }

但这永远不会结束,因为 MoveNext() 永远不会设置为 false。至少调试器跳进了协程方法。

是否有任何易于应用的解决方案来使用 Visual Studio 或 Unity Test Runner 测试我的协程,而无需重构整个项目结构?

【问题讨论】:

    标签: c# unit-testing unity3d coroutine test-runner


    【解决方案1】:

    我试过myMeasuredValues.Normalize()(没用,调试器刚刚跳过)

    当然是这样,因为这不是你运行协程的方式

    StartCoroutine(myMeasuredValues.Normalize)(不起作用,StartCoroutine 在该上下文中不可用)。

    那是因为StartCoroutineMonoBehaviour 的成员。


    您可以做的是引用场景中的任何MonoBehaviour 并让这个处理例程。

    你可以例如有一个专门的课程,例如

    public class TestRoutineRunner : MonoBehaviour
    {
        // implementation of the singleton pattern
        private static TestRoutineRunner instance;
    
        // Get the reference to instance or create it lazy when needed
        public static TestRoutineRunner Instance
        {
            get
            {
                // if instance already exists and is set return it right way
                if(instance) return instance;
    
                // otherwise find it in the scene
                instance = FindObjectOfType<TestRoutineRunner>();
    
                // if it is found in the scene return it now
                if(instance) return instance;
    
                // otherwise create a new one
                instance = new GameObject("TestRoutineRunner").AddComponent<TestRoutineRunner>();
    
                return instance;
            }
        }
    
        // Use this for adding a callback what should be done when the routine finishes
        public void TestRoutine(IEnumerator routine, Action whenDone)
        {
            StartCoroutine(RunRoutine(routine, whenDone));
        }
    
        private IEnumerator RunRoutine(IEnumerator routine, Action whenDone)
        {
            // runs the routine an wait until it is finished
            yield return routine;
    
            // execute the callback
            whenDone?.Invoke();
        }
    }
    

    然后在测试脚本中做

    [Test]
    public void TestNormalization()
    {
        MeasuredValues myMeasuredValues = new MeasuredValues();
    
        ...
    
        // run the routine and pass the assert as callback
        TestRoutineRunner.Instance.TestRoutine(
            myMeasuredValues.Normalize(), 
            // for example as lambda expression
            () =>
            {
                // Assert here
            }
        );
    
        // or as method call
        TestRoutineRunner.Instance.TestRoutine(myMeasuredValues.Normalize(), AssertNow);
    }
    
    private void AssertNow()
    {
        // assert here
    }
    

    【讨论】:

    • 你好 Hugo,我刚刚意识到,它只是部分工作——每次调试器在协程中时,协程只会执行到“yield return null”语句。之后,调用 TearDown 方法。你有关于如何解决这个问题并强制它执行协程直到结束的建议吗?
    • TearDown 方法是什么?
    • TearDown 方法是 Unity Test Runner 调用的方法,参见ilkinulas.github.io/programming/unity/2016/02/20/…(“TearDown 方法在每个测试用例之后调用一次。”)
    猜你喜欢
    • 1970-01-01
    • 2018-12-25
    • 2013-01-16
    • 1970-01-01
    • 2022-11-11
    • 2010-11-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多