编写和运行测试 — MRTK2

为了确保 MRTK 可靠,MRTK 有一组测试来确保代码的更改不会回退现有行为。 大型代码库(如 MRTK)中的良好测试覆盖率对于稳定性和进行更改时具有信心至关重要。

MRTK 使用 Unity 测试运行程序,该程序使用 NUnit的 Unity 集成。 本指南将提供有关如何将测试添加到 MRTK 的基础信息。 它不会介绍可在提供的链接中查看的 Unity 测试运行开发NUnit

提交拉取请求之前,请确保:

  1. 在本地运行测试,以便更改不会回退现有行为(如果任何测试失败,则不会允许完成 PR)。

  2. 如果修复一个 bug,请编写测试来测试该修复程序,并确保以后的代码修改不会再次将其破坏。

  3. 如果编写一个功能,请编写新测试以防止即将发生的代码更改破坏此功能。

当前 playmode 测试应在 Unity 2018.4 中运行,在其他版本的 Unity 中可能会失败

运行测试

Unity 编辑器

可以在“窗口”>“常规”>“测试运行程序”下找到 Unity 测试运行程序,它将显示所有可用的 MRTK 播放和编辑模式测试。

命令行

还可以通过位于 Scripts\test\run_playmode_tests.ps1powershell 脚本运行测试。 这将完全按 github/CI 上执行 playmode 测试的方式运行这些测试(如下所示),并打印结果。 下面是有关如何运行脚本的一些示例

在位于 H:\mrtk.dev 的项目上使用 Unity 2018.4(例如 Unity 2018.4.26f1)运行测试

.\run_playmode_tests.ps1 H:\mrtk.dev -unityExePath "C:\Program Files\Unity\Hub\Editor\2018.4.26f1\Editor\Unity.exe"

在位于 H:\mrtk.dev 的项目上使用 Unity 2018.4,并将结果输出到 C:\playmode_test_out

.\run_playmode_tests.ps1 H:\mrtk.dev -unityExePath "C:\Program Files\Unity\Hub\Editor\2018.4.26f1\Editor\Unity.exe" -outFolder "C:\playmode_test_out\"

还可以通过 run_repeat_tests.ps1 脚本多次运行 playmode 测试。 可以使用在 run_playmode_tests.ps1 中使用的所有参数。

.\run_repeat_tests.ps1 -Times 5

拉取请求验证

MRTK 的 CI 将在所有配置中生成 MRTK,并运行所有编辑和播放模式测试。 如果用户拥有足够的权限,则可以通过在 github PR /azp run mrtk_pr 上发布注释来触发 CI。 可以在 PR 的“检查”选项卡中查看 CI 运行。

只有在成功通过所有测试后,才能将 PR 合并到 main。

压力测试/批量测试

有时测试只会偶尔失败,这可能会使调试变得困难。

若要在本地运行多个测试,请修改相应的脚本。 以下 python 脚本会使此方案变得更便捷。

运行 python 脚本的先决条件是安装了 Python 3.X

对于需要多次执行的单个测试:

[UnityTest]
public IEnumerator MyTest() {...}

在命令行(建议使用 PowerShell)中运行以下命令

cd scripts\tests
# Repeat the test 5 times. Default is 100
python .\generate_repeat_tests.py -n 5 -t MyTest

将输出复制并粘贴到测试文件中。 下面的脚本用于按顺序运行多个测试:

cd scripts\tests
# Repeat the test 5 times. Default is 100
python .\generate_repeat_tests.py -n 5 -t MyTest MySecondTest

新的测试文件现在应包含

[UnityTest]
public IEnumerator A1MyTest0(){ yield return MyTest();}
[UnityTest]
public IEnumerator A2MyTest0(){ yield return MyTest();}
[UnityTest]
public IEnumerator A3MyTest0(){ yield return MyTest();}
[UnityTest]
public IEnumerator A4MyTest0(){ yield return MyTest();}
[UnityTest]
public IEnumerator MyTest() {...}

打开测试运行程序并观察现在可以重复调用的新测试。

编写测试

可以为新代码添加两种类型的测试

  • 播放模式测试
  • 编辑模式测试

播放模式测试

MRTK 播放模式测试能够测试你的新功能如何响应不同的输入源(如手或眼睛)。

新的播放模式测试可以继承 BasePlayModeTests 或以下可使用的主干。

创建新的播放模式测试:

  • 导航到“资产”>“MRTK”>“测试”>“PlayModeTests”
  • 右键单击,选择“创建”>“测试”>“C# 测试脚本”
  • 用以下主干替换默认模板
#if !WINDOWS_UWP
// When the .NET scripting backend is enabled and C# projects are built
// The assembly that this file is part of is still built for the player,
// even though the assembly itself is marked as a test assembly (this is not
// expected because test assemblies should not be included in player builds).
// Because the .NET backend is deprecated in 2018 and removed in 2019 and this
// issue will likely persist for 2018, this issue is worked around by wrapping all
// play mode tests in this check.

using Microsoft.MixedReality.Toolkit.Input;
using Microsoft.MixedReality.Toolkit.Utilities;
using NUnit.Framework;
using System;
using System.Collections;
using System.Linq;
using UnityEngine;
using UnityEngine.TestTools;

namespace Microsoft.MixedReality.Toolkit.Tests
{
    class ExamplePlayModeTests
    {
        // This method is called once before we enter play mode and execute any of the tests
        // do any kind of setup here that can't be done in playmode
        public void Setup()
        {
            // eg installing unity packages is only possible in edit mode
            // so if a test requires TextMeshPro we will need to check for the package before entering play mode
            PlayModeTestUtilities.InstallTextMeshProEssentials();
        }

        // Do common setup for each of your tests here - this will be called for each individual test after entering playmode
        // Note that this uses UnitySetUp instead of [SetUp] because the init function needs to await a frame passing
        // to ensure that the MRTK system has had the chance to fully set up before the test runs.
        [UnitySetUp]
        public IEnumerator Init()
        {
            // in most play mode test cases you would want to at least create an MRTK GameObject using the default profile
            TestUtilities.InitializeMixedRealityToolkit(true);
            yield return null;
        }

        // Destroy the scene - this method is called after each test listed below has completed
        // Note that this uses UnityTearDown instead of [TearDown] because the init function needs to await a frame passing
        // to ensure that the MRTK system has fully torn down before the next test setup->run cycle starts.
        [UnityTearDown]
        public IEnumerator TearDown()
        {
            PlayModeTestUtilities.TearDown();
            yield return null;
        }

        #region Tests

        /// <summary>
        /// Skeleton for a new MRTK play mode test.
        /// </summary>
        [UnityTest]
        public IEnumerator TestMyFeature()
        {
            // ----------------------------------------------------------
            // EXAMPLE PLAY MODE TEST METHODS
            // ----------------------------------------------------------
            // Getting the input system
            // var inputSystem = PlayModeTestUtilities.GetInputSystem();

            // Creating a new test hand for input
            // var rightHand = new TestHand(Handedness.Right);
            // yield return rightHand.Show(new Vector3(0, 0, 0.5f));

            // Moving the new test hand
            // We are doing a yield return here because moving the hand to a new position
            // requires multiple frames to complete the action.
            // yield return rightHand.MoveTo(new Vector3(0, 0, 2.0f));

            // Getting a specific pointer from the hand
            // var linePointer = PointerUtils.GetPointer<LinePointer>(Handedness.Right);
            // Assert.IsNotNull(linePointer);
            // ---------------------------------------------------------

            // Your new test here
            yield return null;
        }
        #endregion
    }
}
#endif

编辑模式测试

编辑模式测试在 Unity 的编辑模式下执行,可以添加到混合现实工具包存储库中的“MRTK”>“测试”>“EditModeTests”文件夹下。 若要创建新测试,可以使用以下模板:

// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using NUnit.Framework;

namespace Microsoft.MixedReality.Toolkit.Tests
{
    class EditModeExampleTest
    {
        [Test]
        /// the name of this method will be used as test name in the unity test runner
        public void TestEditModeExampleFeature()
        {

        }
    }
}

测试命名约定

通常应根据测试所测试的类或方案来命名测试。 例如,给定一个待测试的类:

namespace Microsoft.MixedReality.Toolkit.Input
{
    class InterestingInputClass
    {
    }
}

考虑命名测试

namespace Microsoft.MixedReality.Toolkit.Tests.Input
{
    class InterestingInputClassTest
    {
    }
}

考虑将测试放在类似于其对应非测试文件的文件夹层次结构中。 例如:

Non-Test: Assets/MRTK/Core/Utilities/InterestingUtilityClass.cs
Test: Assets/MRTK/Tests/EditModeTests/Core/Utilities/InterestingUtilityClassTest.cs

这是为了确保有一个明确的方法来查找每个类的相应测试类(如果存在这样的测试类)。

基于方案的测试的位置定义较少 - 例如,如果测试使用整个输入系统,请考虑将其放入相应的编辑模式或播放模式测试文件夹中的“InputSystem”文件夹。

测试脚本图标

添加新测试时,请修改脚本以使其具有正确的 MRTK 图标。 有一个简单的 MRTK 工具可以实现此目的:

  1. 转到“混合现实工具包”菜单项。
  2. 依次单击“实用工具”、“更新”、“图标”。
  3. 单击“测试”,更新程序将自动更新,并更新缺少其图标的任何测试脚本。

MRTK 实用工具方法

本部分介绍了编写 MRTK 的测试时常用的一些代码片段/方法。

有两个实用工具类可帮助设置 MRTK 和测试与 MRTK 中的组件的交互

TestUtilities 提供以下方法来设置 MRTK 场景和 Gameobject:

/// creates the mrtk GameObject and sets the default profile if passed param is true
TestUtilities.InitializeMixedRealityToolkit()

/// creates an empty scene prior to adding the mrtk GameObject to it
TestUtilities.InitializeMixedRealityToolkitAndCreateScenes();

/// sets the initial playspace transform and camera position
TestUtilities.InitializePlayspace();

/// destroys previously created mrtk GameObject and playspace
TestUtilities.ShutdownMixedRealityToolkit();

请参阅 TestUtilitiesPlayModeTestUtilities 的 API 文档,以获取这些实用类的更多方法,因为在 MRTK 中添加新测试时,这些类会定期扩展。