基于 unity pro 2021.3 实现。

安装 Test Framework

打开 Window — Package Manager,Packages选择Unity Registry,搜索Test Framework
如果没有安装,需要在包管理器中安装一下。

编写和运行单测

编辑器模式(Editor Mode)和运行模式(Play Mode)

编辑器模式的单测仅在Unity编辑器中执行代码,并不运行场景,此外除了游戏代码外还可以调用编辑器相关API。
使用编辑模式测试,可以将测试用例与以 UnityTest 属性标记,可以测试任何编辑器扩展,在这种测试下,测试代码在EditorApplication.update回调循环中运行。如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// A Test behaves as an ordinary method
[Test]
public void BSplineTestSimplePasses()
{

// Use the Assert class to test conditions
}

// A UnityTest behaves like a coroutine in Play Mode. In Edit Mode you can use
// `yield return null;` to skip a frame.
[UnityTest]
public IEnumerator BSplineTestWithEnumeratorPasses()
{

// Use the Assert class to test conditions.
// Use yield to skip a frame.
yield return null;
}

运行模式的单测则可以在目标平台或Unity编辑器中建立一个临时的场景,以运行状态执行测试用例。
同样运行模式也可以使用 UnityTest 属性标记用例,如果标记了该属性,测试将作为协程运行,可以通过协程等待一些诸如下载资源、时间延时、帧渲染等工作完成后,再执行后边的逻辑。

1
2
3
4
5
6
7
8
9
10
[UnityTest]
public IEnumerator GameObject_WithRigidBody_WillBeAffectedByPhysics()
{

var go = new GameObject();
go.AddComponent<Rigidbody>();
var originalPosition = go.transform.position.y;

yield return new WaitForFixedUpdate();
Assert.AreNotEqual(originalPosition, go.transform.position.y);
}

详见文档 edit-mode-vs-play-mode

创建测试模块

想要使用Test Framework进行单测,首先需要创建测试模块,Unity提供的GUI来完成这个创建的过程。
打开 window - general - Test Runner
image.png
此时,若已经检测到了测试模块,则Test Runner中会显示case列表,若没有检测到测试模块,则会显示创建模块按钮:
image.png
根据不同的单测模式,点击 Create PlayMode/EditMode Test Assembly Folder,则会创建对应的测试模块目录。
打开IDE可以看到Unity已经创建好了Test目录,并生成了一个模块声明文件 Test.asmdef

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
{
"name": "Tests",
"rootNamespace": "",
"references": [
"UnityEngine.TestRunner",
"UnityEditor.TestRunner",
],

"includePlatforms": [
"Editor"
],

"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": true,
"precompiledReferences": [
"nunit.framework.dll"
],

"autoReferenced": false,
"defineConstraints": [
"UNITY_INCLUDE_TESTS"
],

"versionDefines": [],
"noEngineReferences": false
}

创建测试脚本

测试模块创建好后,Unity提供了一个快速创建测试脚本样例的按钮
image.png
点击即可创建好示例测试用例代码。

若想继续用GUI创建测试目录&测试脚本,可以

  • 在 Project 窗口, 选择 Assets
  • 顶部菜单中 Assets > Create> Testing > Tests Assembly Folder,即可创建新的测试模块目录
  • 在 Project 窗口, 选择测试模块目录
  • 顶部菜单中 Assets > Create > Testing > C# Test Script,即可创建新的测试脚本

当然,后续的case照着系统生成的模板自己手写也是OK的。

关联用户模块

在单元测试中,若想 using 用户模块的类型,Editer中会报错
image.png
这是因为测试模块未关联用户代码模块。
首先,若用户代码未创建模块定义,则需要在Unity的Project窗口中,右键用户代码目录,创建模块定义
image.png
然后点击测试模块定义文件Test.asmdef ,添加用户代码模块关联
image.png
此时即可正常using用户类型。
也可以不通过GUI,直接在Test.asmdef中手动添加关联

1
2
3
4
5
   "references": [
"UnityEngine.TestRunner",
"UnityEditor.TestRunner",
"vhsdk",
],

运行测试用例

通过GUI运行

打开Test Runner后,可以Run All 以执行所有单测case
image.png
也可以单条执行case
image.png

通过脚本运行

可以通过 TestRunnerApi 来通过脚本启动单测,如:

1
2
3
4
5
6
var testRunnerApi = ScriptableObject.CreateInstance<TestRunnerApi>();
var filter = new Filter()
{
testMode = TestMode.PlayMode
};
testRunnerApi.Execute(new ExecutionSettings(filter));

参考文档:How to run tests programmatically

通过命令运行

1
Unity.exe -runTests -batchmode -projectPath PATH_TO_YOUR_PROJECT -testResults C:\temp\results.xml -testPlatform PS4

batchmode选项将会强制 Unity Editor 用无头模式(精简模式)。当只需要运行播放器性能测试,且不需要完全打开 Unity Editor 时,可以使用这个参数。这在自动执行测试时会节约大量的时间。如果不使用这个选项,那么 Unity Editor 在开始测试前会完全打开。

参考文档:Running tests from the command line

MonoBehaviour单测

MonoBehaviour的对象不能直接new出来,需要添加 Gameobject,然后 AddComponent 的方式来获取实例。

1
2
3
4
5
6
[UnityTest]
public IEnumerator TestAddPhones() {
GameObject obj = new GameObject("BaseBlendShapeController");
BaseBlendShapeController bsController = obj.AddComponent<TestBlendShapeController>();
yield return null;
}

此外,Test Framework 针对 MonoBehaviour 的单测提供了一些辅助性的类和接口,用于自定义一些测试结束的条件,如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[UnityTest]
public IEnumerator MonoBehaviourTest_Works()
{
yield return new MonoBehaviourTest<MyMonoBehaviourTest>();
}

public class MyMonoBehaviourTest : MonoBehaviour, IMonoBehaviourTest
{
private int frameCount;
public bool IsTestFinished
{
get { return frameCount > 10; }
}

void Update()
{
frameCount++;
}
}

上面的例子中,如果有需要,可以在 MyMonoBehaviourTest中实现测试相关的逻辑,然后通过IsTestFinished变量来控制测试的结束。
参考文档:MonoBehaviour tests

覆盖率统计

Unity提供了 Code Coverage 组件,可以配合 Test Runner 使用,生成覆盖率报告。
参考文档:Code Coverage Tutorial

Code Coverage安装

同单测模块一样,在包管理器中搜索安装即可。
image.png

配合Test Runner生成覆盖率报告

使用GUI统计

Window > Analysis > Code Coverage 中打开覆盖率工具,勾选 Enable Code Coverage,并且设置好需要统计覆盖率的模块(需要和’关联用户模块’小节中一样创建好模块定义)。
image.png
勾选enable后,每次执行完测试会自动生成报告,也可以不勾选enable,在右下角的地方手动启动和停止监听以生成报告。

使用API统计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
using UnityEngine;
using UnityEditor;
using UnityEditor.TestTools.CodeCoverage;
using UnityEditor.SceneManagement;

public class CoverageApiTest : MonoBehaviour
{
[MenuItem("CodeCoverage/Run Recording")]
static void RunRecording()
{

CodeCoverage.VerbosityLevel = LogVerbosityLevel.Verbose;

int i;

EditorSceneManager.OpenScene("Assets/Scenes/Scene1.unity");

CodeCoverage.StartRecording();

for (i = 0; i < 1000; ++i)
{
Instantiate(Resources.Load("ComplexPrefab1"));
}

CodeCoverage.PauseRecording();

EditorSceneManager.OpenScene("Assets/Scenes/Scene2.unity");

CodeCoverage.UnpauseRecording();

for (i = 0; i < 1000; ++i)
{
Instantiate(Resources.Load("ComplexPrefab2"));
}

CodeCoverage.StopRecording();
}
}
Properties

参考文档:Class CodeCoverage

使用命令行模式统计

1
Unity.exe -projectPath <path-to-project> -batchmode -testPlatform editmode -runTests -testResults
<path-to-results-xml> -debugCodeOptimization 
-enableCodeCoverage
-coverageResultsPath <path-to-coverage-results>
-coverageHistoryPath <path-to-coverage-history>
-coverageOptions "generateAdditionalMetrics;generateHtmlReport;generateHtmlReportHistory;generateBadgeReport;
assemblyFilters:+my.assembly.*,+<packages>;
pathFilters:-**/Tests/**,-**/BuiltInPackages/**;
verbosity:verbose"

参考文档:CoverageBatchmode

覆盖率报告

image.png

☞ 参与评论