目录
Unity 基础 之 简单介绍与使用 单元测试 Test Runner / Assert 基于 2019.3.x
一、简单介绍
二、单元测试分为两个模式:Edit Mode 和 Play Mode
二、Assert 简单介绍
三、Edit Mode 单元测试的使用
注意:
简单使用步骤实现
关键代码
四、Play Mode 单元测试的使用
注意:
简单使用步骤实现
关键代码
五、Test Runer 面板简单说明
六、整理总结:
七、参考资料
Unity中的一些基础知识点。
本节简单介绍单元测试,并简单的进行一些使用,可能有疏漏,如果您有更好的方法,欢迎留言指正。
单元测试的可靠性
我们的目标是写出可靠、可维护、可读的测试。
因此,除了遵循单元测试结构规范编写单元测试之外,我们还需要注意可靠性、可维护性以及可读性这些方面。因此,一些原则我们也需要注意。
不轻易删除和修改测试
一旦测试写好了并且通过了,就不应该轻易的修改和删除这些测试。因为这些测试是对应系统代码的保护伞,在修改系统代码时,这些测试会告诉我们修改后的代码是否会破坏已有的功能。
尽量避免测试中的逻辑
随着测试中的逻辑增多,测试代码出现缺陷的几率也会增大。而且由于我们往往相信测试是可靠的,因此一旦测试出现缺陷我们往往不会首先考虑是测试的问题,可能会浪费时间去修改系统代码。而单元测试中,最好保持逻辑的简单,因此尽量避免使用下面的逻辑控制代码。
1.switch、if
2.foreach、for、while
一个单元测试应该是一系列的方法调用和断言,但是不应该包含控制流语句。
只测试一个关注点
在一个单元测试中验证多个关注点会使得测试代码变得复杂,但却没有价值。相反,我们应该在分开的、独立的单元中验证多余的关注点,这样才能发现真正导致失败的地方。
单元测试的可维护性
去除重复代码
和系统中的重复代码一样,在单元测试中重复代码同样意味着测试对象某方面改变时要修改更多的测试代码。
如果测试看上去都一样,仅仅是参数不同,那么我们完全可以使用参数化测试即使用[TestCase]特性将不同的数据作为参数传入测试方法。
实施测试隔离
所谓的测试隔离,指的是一个测试和其他的测试隔离,甚至不知道其他测试的存在,而只在自己的小世界中运行。
将测试隔离的目的是防止测试之间的互相影响,常见的测试之间互相影响的情况可以总结如下:
1、强制的测试顺序:测试要以某种顺序执行,后一个测试需要前面的测试结果,这种情况有可能会导致问题的原因是因为NUnit不能保证测试按照某种特定的顺序执行,因此今天通过的测试,明天可能就不好用了
2、隐藏的测试调用:测试调用其他测试
3、共享状态被破坏:测试要共享状态,但是在一个测试完成之后没有重置状态,进而影响后面的测试
单元测试的可读性
正如概述中所说单元测试是一种无价的文档,它是展示方法或类如何使用的最佳文档。因此,可读性这条要求的重要性便可见一斑。试想一下即便是几个月之后别的程序员都可以通过单元测试来理解一个系统的组成以及使用方法,并能够很快的理解他们要做的工作以及在哪里切入。
单元测试命名
在单元测试的结构中已经有过要求和介绍。参考那部分。
单元测试中的变量命名
通过合理的命名变量,可以提高可读性,使得阅读测试的人员可以尽快的理解你要验证的内容。
官方网址介绍:https://docs.unity3d.com/2018.2/Documentation/Manual/testing-editortestsrunner.html
官方网址介绍:https://docs.unity3d.com/2019.3/Documentation/ScriptReference/Assertions.Assert.html
官方网址介绍:https://docs.unity3d.com/2018.2/Documentation/Manual/testing-editortestsrunner.html
1)创建 Editor 文件夹,在 Editor 文件夹下即可创建 Testing 相关脚本
2)Edit Mode 可以引用自己其他文件夹下的脚本代码
3)可以不用创建 Tests Assembly Folder
4)UnityTest IEnumerator 测试中只能是 yield return null,其他或报错
1、创建一个 Editor 和 MyClass 文件夹,Editor 用于编写单元测试代码,MyClass 编写自己的代码脚本
2、MyClass 文件夹下新建一个脚本,编写简单代码,用于测试
3、选中 Editor 文件夹,右键新建 Create - Testing - C# Test Script
4、编写代码,测试 MyClass 函数,以及故意Assert 出错的案例
5、菜单Windows - General - Test Runner ,打开 Test Runner
6、点击 Run All 或者双击指定的测试用例也可以跑,结果如预期
1)MyClass
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace MyTestCalss {
public class MyClass
{
public int Add(int a, int b) {
return (a) + (b);
}
public int SubResult;
public IEnumerator Sub(int a,int b) {
yield return null;
SubResult = (a) - (b);
}
}
}
2)EditModeTests
using System.Collections;
using System.Collections.Generic;
using MyTestCalss;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
namespace Tests
{
public class EditModeTests
{
private MyClass mMyClass;
[SetUp]
public void SetUp()
{
mMyClass = new MyClass();
}
// A Test behaves as an ordinary method
[Test]
public void EditModeTestsSimplePasses()
{
// Use the Assert class to test conditions
int result = mMyClass.Add(1, 3);
Debug.Log("Add(1,3) = " + result) ;
Assert.AreEqual(result, 4);
}
// 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 EditModeTestsWithEnumeratorPasses()
{
// Use the Assert class to test conditions.
// Use yield to skip a frame.
yield return null;
yield return mMyClass.Sub(3,1);
Debug.Log("Sub(3,1) = " + mMyClass.SubResult);
Assert.AreEqual(mMyClass.SubResult, 2);
}
}
public class EditModeTests2
{
// A Test behaves as an ordinary method
[Test]
public void EditModeTestsSimplePasses()
{
// Use the Assert class to test conditions
Assert.IsFalse(true);
}
// 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 EditModeTestsWithEnumeratorPasses()
{
// Use the Assert class to test conditions.
// Use yield to skip a frame.
yield return null;
Assert.IsEmpty(null);
}
}
}
官方网址介绍:https://docs.unity3d.com/2018.2/Documentation/Manual/testing-editortestsrunner.html
1)可以在任意位置创建,但是建议新建一个脚本 PlayModeTests,便于管理
2)好似 Edit Mode 创建运行成功之后,才可不需要 Tests Assembly Folder 就可任意任意位置创建 PlayModeTests(有待进一步确定)
3)可以不用创建 Tests Assembly Folder
4)Tests Assembly Folder 创建以后,好似 引用不到该文件夹外的自定义脚本代码
5)特定Tests场景会自动运行与结束
6)Test Runners 窗口右侧的下拉菜单中选中 Enable play mode tests for all assemblies,注意在发布游戏前要把这个选项关掉,否则测试相关的内容也会被编译到最终文件中
1、创建一个文件夹PlayModeTests,并创建一个测试脚本
2、在 MyClass 文件夹 新建一个脚本,用于 Play Mode 模式下测试
3、编写 PlayModeTests 测试脚本,五个用例,一个 Ignore,一个不通过,三个通过
4、菜单Windows - General - Test Runner ,打开 Test Runner
5、点击 Run All 或者双击指定的测试用例也可以跑,结果如预期
1)MyMonoClass
using System.Collections;
using System.Collections.Generic;
using MyTestCalss;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
namespace Tests
{
public class EditModeTests
{
private MyClass mMyClass;
[SetUp]
public void SetUp()
{
mMyClass = new MyClass();
}
// A Test behaves as an ordinary method
[Test]
public void EditModeTestsSimplePasses()
{
// Use the Assert class to test conditions
int result = mMyClass.Add(1, 3);
Debug.Log("Add(1,3) = " + result) ;
Assert.AreEqual(result, 4);
}
// 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 EditModeTestsWithEnumeratorPasses()
{
// Use the Assert class to test conditions.
// Use yield to skip a frame.
yield return null;
yield return mMyClass.Sub(3,1);
Debug.Log("Sub(3,1) = " + mMyClass.SubResult);
Assert.AreEqual(mMyClass.SubResult, 2);
}
}
public class EditModeTests2
{
// A Test behaves as an ordinary method
[Test]
public void EditModeTestsSimplePasses()
{
// Use the Assert class to test conditions
Assert.IsFalse(true);
}
// 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 EditModeTestsWithEnumeratorPasses()
{
// Use the Assert class to test conditions.
// Use yield to skip a frame.
yield return null;
Assert.IsEmpty(null);
}
}
}
2)PlayModeTests
using System.Collections;
using System.Collections.Generic;
using MyTestCalss;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
namespace Tests
{
public class PlayModeTests
{
private MyMonoClass mMyMonoClass;
[SetUp]
public void SetUp()
{
mMyMonoClass = new GameObject("MyMonoClass").AddComponent();
}
[Ignore("测试 Ignore")]
public void PlayModeTestIgnore() {
Assert.AreEqual(mMyMonoClass.mTestCounter, 0);
}
// A Test behaves as an ordinary method
[Test]
public void PlayModeTestsSimplePasses()
{
// Use the Assert class to test conditions
int result = mMyMonoClass.mTestCounter;
Debug.Log("mMyMonoClass.mTestCounter = " + result);
Assert.AreNotEqual(result, 0);
}
// 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 PlayModeTestsWithEnumeratorPasses()
{
// Use the Assert class to test conditions.
// Use yield to skip a frame.
yield return null;
yield return mMyMonoClass.Test();
Debug.Log("mMyMonoClass.mTestTime = " + mMyMonoClass.mTestTime);
Debug.Log("mMyMonoClass.mTestCounter = " + mMyMonoClass.mTestCounter);
Assert.AreEqual(mMyMonoClass.mTestTime, 3);
}
}
public class PlayModeTests2
{
private MyClass mMyClass;
[SetUp]
public void SetUp()
{
mMyClass = new MyClass();
}
// A Test behaves as an ordinary method
[Test]
public void PlayModeTestsSimplePasses()
{
// Use the Assert class to test conditions
int result = mMyClass.Add(1, 3);
Debug.Log("Add(1,3) = " + result);
Assert.AreEqual(result, 4);
}
// 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 PlayModeTestsWithEnumeratorPasses()
{
// Use the Assert class to test conditions.
// Use yield to skip a frame.
yield return null;
yield return mMyClass.Sub(3, 1);
Debug.Log("Sub(3,1) = " + mMyClass.SubResult);
Assert.AreEqual(mMyClass.SubResult, 2);
}
}
}
Run All:测试全部用例
Run Selected:测试选中的用例
Rerun Failed: 重新测试上一次未通过的测试用例
搜索框:可以搜索用例
种类过滤器:可以根据种类来筛选用例。种类需要在测试代码中使用CategoryAttribute来标识
测试结果筛选器:可以按照通过、失败以及忽略来筛选用例
PlayMode / EditMode :Play 模式和 Edit 模式面板切换
1、Edit Mode 测试应该放在 Editor 目录或其子目录下。
2、Play Mode 测试放在非 Editor 目录下,建议放 Assets/PlayModeTests,并需要在 Test Runners 窗口右侧的下拉菜单中选中 Enable play mode tests for all assemblies。注意在发布游戏前要把这个选项关掉,否则测试相关的内容也会被编译到最终文件中。
3、测试函数要标上 [Test] 或者 [UnityTest] 属性才会被 Test Runner 识别,前者是普通测试,后者具有跳过帧的能力(可使用 yield return null,根据测试模式决定是跳 EditorApplication.update 还是 update)。
4、[UnityTest] 在 Edit Mode 中是跳过 EditorApplication.update, 在 PlayMode 中是跳过 Monobehavior 的 Update。要测试涉及到游戏内 Update 相关内容时,必须将测试写为 PlayMode 测试; Edit Mode 的测试只用于与 Update 无关或者与 Editor 的 Update 相关的内容(主要是编辑器插件关注)。
5、 总结:显式或涉及 Update 的测试内容全部需要放在 Play Mode 下,其他的在 Edit Mode 下。
6、Play Mode 测试运行时会新建临时场景,运行后自动删除,而 Edit Mode 则不会。
7、使用 Assert 类语法对关注的值进行测试,具体可用内容参见 NUnit 文档。
参考网址:https://github.com/nunit/docs/wiki/NUnit-Documentation
参考博文:https://blog.csdn.net/jinxiul5/article/details/82114407
Unity Test Runner 官方手册:https://docs.unity3d.com/Manual/testing-editortestsrunner.html
https://docs.unity3d.com/Manual/testing-editortestsrunner.html
https://docs.unity3d.com/Manual/PlaymodeTestFramework.html
使用NUnit为Unity3D编写高质量单元测试的思考:https://www.gameres.com/668571.html
TDD在Unity3D游戏项目开发中的实践:https://www.gameres.com/497930.html
Unity 单元测试(NUnit,UnityTestTools):https://www.cnblogs.com/plateFace/p/5176961.html
在Unity中写单元测试:https://blog.csdn.net/kingsea168/article/details/49583083