UnityTestTool实用解释
2015-11-16 17:13
507 查看
UnityTestTool实用解释
原文地址:http://www.jianshu.com/p/102e2459604e
概述
以下的场景是否似曾相识:你说:“这模块我不熟啊!让我去改,会不会引起其他问题啊?算了,都review好几遍跑好几遍了应该没问题。就让测试同学去测好了。”然后,然后这个改动没被测出并引起了外网的crash。
你说:“这个模块怎么用啊?看了注释、文档后还是一头雾水。唉,还是去搜搜别人的用到这个模块的代码吧。”然后,然后你花了20分钟,从散落四处的使用者代码,终于总结出咋用这个模块。
你说: “只花了一天时间,就设计好、写好一个模块了,我太棒了!”,用你模块的同学说:“你这个模块的接口不好用啊,应该那样更好吧!”,然后,然后你又花了一天重新设计了接口。
测试用例能够解决上面这些的问题。在Unity中,UnityTestTool是编写测试用例的工具。
UnityTestTool简介
UnityTestTool是Unity提供的官方测试工具,它的核心功能包括:测试用例工具
单元测试(Unit Test):测试纯代码的用例。单元测试“运行于Editor”,即不依赖于任何场景,不需场景在Play状态。
比如上图,每一行都是一个测试函数,以及每个函数的测试结果。
集成测试(Integration Test):测试GameObject(以下简称GO)的用例。集成测试“运行于Engine”,即依赖于场景且需要场景在Play状态才能测。
比如上图左部场景里,右边有测试结果icon的GO都是集成测试用例(比如图中的“Test2-TimeOut”)。需要被测试的GO(比如图中的“Sphere”)作为测试用例GO的孩子节点。集成测试用例GO本身挂接了进行测试的脚本代码,对被测试GO进行判断以决定测试用例是否成功。一个场景里允许有多个集成测试用例,但一个时刻只有一个测试用例是Activated的(留意图中只有“Test3 - Failur”是白色,其他是灰色的)。
其他工具
断言组件(AssertionComponent):往GameObject添加AssertionComponent,以监控GameObject的指定状态。当AssertionComponent监控的状态不符合断言时,将抛出异常。
上图为挂接在MainCamera的一个断言组件,它断言MainCamera的drag值必须等于一个GameObject的drag,否则,将抛出异常。抛出异常后,如果在console面板设置了“Pause on error”,将自动暂停场景,实现了类似断点的功能;也正因可以抛出异常,所以断言组件可以和集成测试用例配合使用。
测试用例
测试用例能做什么
测试用例之所以能解决一开始提出的那些问题,是因为:测试用例可以验证功能实现是否正常:前提是测试用例本身设计正确。
测试用例和功能实现是高度匹配的:模块A的功能实现和模块A的测试用例,是由同一个(批)人,在同一个时间段内编写的。另外,更改功能的接口时,需要同时更改测试用例。
测试用例本身就是实用文档:测试用例直接使用代码的形式描述了该怎么使用模块、和使用模块的注意点(因为测试用例使用抛出异常的方法来描述不正确使用模块的情况)。
测试驱动:测试用例甚至可以反过来驱动功能实现。测试用例代码比功能实现代码简单得多,且直接面对功能模块的接口。所以先写测试用例再写功能实现能帮助开发者整理思路、设计更加稳妥的接口。
使用测试用例进行测试是简单的、可自动化的:工具如果设计恰当,只需简单点击、甚至使用脚本自动化,就完成了测试。另,可以在迭代的某些关键时刻进行自动化测试,比如:每次组员提交代码的时刻、每整点、每次构建版本时。
测试用例是永久性的:不同于口口相传的瞬时性。(当然文档、代码注释也是永久性的)。
测试用例不能做什么
测试用例只是开发阶段的测试,其不能代替后面测试阶段的真正人肉集成测试。
测试用例的心理纠结
“写测试用例太麻烦、太浪费时间啦!用不用测试用例呢?”。其实就是一个“重要事情”和“紧急事情”的权衡。都是有生活经验的人了,如何权衡,应该都懂。
总之,建议,在没养成写测试用例的习惯之前、在没体验过测试用例到底花费多少时间之前,都不要武断给下结论“不写测试用例”。
UnityTestTool使用方法
准备工具
下载UnityTestTool,并导入到你的unity项目工程中
编写单元测试
创建单元测试代码文件。注意由于单元测试是不依赖于场景的,所以需要放在Editor目录下(因为项目中任意一个“Editor”目录都被Unity认为是特殊的“编辑器目录”)。编辑单元测试代码
引用
NUnit.Framework
给需要被测试的类添加
[TestFixture]标签,给需要被测试的方法添加
[Test]标签。
被测试的方法注意是无参数、无返回的函数。
如果认为有异常发生,通过
throw new Exception("异常描述");
using NUnit.Framework; using System; using MoreFun.Collections; namespace MoreFun.Editor.Test.Collections { [TestFixture] class BasicTreeTest { [Test] public void AddAndRemoveChild() { TreeData data = new TreeData(); BasicTree tree1 = new BasicTree(data); TreeData data2 = new TreeData(); BasicTree tree2 = new BasicTree(data2); tree1.AddChild(tree2); if (tree1.GetChildrenCount() != 1) { throw new Exception("测试失败"); } //省略剩下代码... } } }
在Unity打开UnityTestTool的单元测试面板,会发现刚写的测试代码已被自动添加到面板中。点击单元测试面板的播放键,就进行(需要人手UI操作的)单元测试了。
编写集成测试
创建测试场景在Unity打开UnityTestTool的集成测试面板。点击“+”号,在这个测试场景添加一个测试用例GO(比如上面的BattleMessageTest和ActorTest)
给测试用例GO(比如ActorTest)下添加具体的GO(比如图中的“GameObject”),以及给GO添加测试脚本(比如图中的ActorTest)
在测试脚本调用
IntegrationTest.Pass(),让测试用例通过;在测试脚本抛出异常,让测试不通过。
也可以配置测试用例GO里的具体参数
脚本自动化
使用脚本自动化的关键无非下面几点:Unity可以以命令行模式进行运行,而且这时能够执行任意一个静态类的静态方法(详见这里)。
UnityTestTool提供了脚本执行的接口
UnityTest.Batch.RunUnitTests()和
UnityTest.Batch.RunIntegrationTests()。
注:
RunIntegrationTests()本只能是从命令行取得需要测试的场景列表。建议修改
RunIntegrationTests()的参数,允许以函数参数的方式传入需要测试的场景列表。
自动测试的结果会以xml的格式输出成文件。所以自动测试的后续流程(比如构建流程)可以依赖于生成的xml文件,判断测试是否通过,再决定是否真正执行。
具体脚本可参见最下面的附录。
更多
更多详细用法,可以阅读导入到工程的UnityTestTool/Docs/下的pdf。
附录
自动执行单元测试用例、集成测试用例的bat脚本:echo "Start Unity Test Case" %unity% -batchmode -projectPath %projectPath% -executeMethod CommandBuild.RunUnitTests -resultFilePath=%buildPath%/BuildTemp -quit -logFile %buildPath%/BuildTemp/RunUnitTests.log %unity% -batchmode -projectPath %projectPath% -executeMethod CommandBuild.RunIntegrationTests -targetPlatform=%testTargetPlatform% -resultsFileDirectory=%buildPath%/BuildTemp -quit -logFile %buildPath%/BuildTemp/RunIntegrationTests.log echo "End Unity Test Case, please see log: BuildTemp/RunUnitTests.log, BuildTemp/RunIntegrationTests.log" echo "Start Build Unity to App" %unity% -batchmode -projectPath %projectPath% -executeMethod CommandBuild.PreBuild %debugParam% -quit -logFile %buildPath%/BuildTemp/PreBuild.log %unity% -batchmode -projectPath %projectPath% -executeMethod CommandBuild.Build %debugParam% -android -buildPath=%buildPath% -quit -logFile ./BuildTemp/Build.log echo "End Build, please see log BuildTemp/PreBuild.log and BuildTemp/Build.log"
自动执行单元测试用例、集成测试用例的C#代码:
using UnityEngine; using UnityEditor; public class CommandBuild { private static string[] ms_scenes = { "Assets/Scenes/KillerStarter.unity" }; private static System.Collections.Generic.List<string> ms_lstTestScenes = new System.Collections.Generic.List<string>() { "Assets/KillerInteTest/TestBattle/TestBattle.unity", "Assets/KillerInteTest/TestUIBase/TestUIBase.unity" }; /// <summary> /// 执行UnityTestTool的单元测试 /// </summary> public static void RunUnitTests() { UnityTest.Batch.RunUnitTests(); } /// <summary> /// 执行UnityTestTool的集成测试 /// </summary> public static void RunIntegrationTests() { UnityTest.Batch.RunIntegrationTests(ms_lstTestScenes); } /// <summary> /// 检测单元测试、集成测试输出的所有xml /// </summary> /// <returns></returns> private static bool CheckTestResult() { UpdateBuildTempFolderPath(); string[] lstXml = System.IO.Directory.GetFiles(ms_buildTempFolder, "*.xml"); if(0 == lstXml.Length) { Debug.Log("在" + ms_buildTempFolder + "目录未找到任何单元测试结果xml文件!"); return false; } else { foreach(string oneXmlFile in lstXml) { string oneXmlContent = System.IO.File.ReadAllText(oneXmlFile).ToLower(); if (oneXmlContent.Contains("success=\"false\"") || oneXmlContent.Contains("result=\"error\"") ) { Debug.Log("找到单元测试失败结果!在" + oneXmlFile + "。请查阅单元测试结果xml文件!"); return false; } } } Debug.Log("未检测到单元测试失败结果!检测文件列表:\n" + lstXml.JoinToString("\n")); return true; } public static void Build() { Debug.Log("Build"); if (false == CheckTestResult()) { throw new System.Exception("检测到测试用例结果失败!终止构建!"); } // 省略以下构建代码 } }
相关文章推荐
- Unity5 发送广播与消息的探索
- Vector方法magnitude
- unity 使用xcode5.1 launching iOS project via Xcode5 failed
- Unity3d官方单元测试插件学习
- Spine导入Unity
- Unity3D教程宝典之两步实现超实用的XML存档
- Unity3D教程:换装方法
- Unity5 关于修改组件GameObject的Color与mainTexture的探索
- UnityEditor中建立两个Color,实现mesh的颜色渐变(Gradient)(GUI.changed,SceneView.RepaintAll())
- Unity中用到的C#补充(五)- 简单的本地存储
- Unity中用到的C#补充(四)- Array,ArrayList,List,Drictionary,Hashtable
- Unity3D 语句 objCube.GetComponent<Renderer>().material.color 报错
- Unity3D游戏开发游戏读/存档在Unity3D中的实现
- 在Unity3D中加载外部图片的两种方法
- Unity3D 有限状态机(一)
- Unity中用到的C#补充(三)-字符串
- Unity3D 委托和事件的优点(一)
- 【笨木头Unity】入门之旅008:Demo之四处找死(三)_触发器
- UnityEditor中新建一个窗口,在Hierarchy中点击一个GameObject时,窗口中就显示相应的GameObject名称和Position
- 002-unity 资源及资源类型 srt字幕文件