您的位置:软件测试 > 开源软件测试 > 开源单元测试工具 > Nunit
使用NUnit为游戏项目编写高质量单元测试的思考
作者:网络转载 发布时间:[ 2016/7/21 13:54:19 ] 推荐标签:单元测试 NUnit

  0x04 单元测试的可靠性
  我们的目标是写出可靠、可维护、可读的测试。
  因此,除了遵循单元测试结构规范编写单元测试之外,我们还需要注意可靠性、可维护性以及可读性这些方面。因此,一些原则我们也需要注意。
  不轻易删除和修改测试
  一旦测试写好了并且通过了,不应该轻易的修改和删除这些测试。因为这些测试是对应系统代码的保护伞,在修改系统代码时,这些测试会告诉我们修改后的代码是否会破坏已有的功能。
  尽量避免测试中的逻辑
  随着测试中的逻辑增多,测试代码出现缺陷的几率也会增大。而且由于我们往往相信测试是可靠的,因此一旦测试出现缺陷我们往往不会首先考虑是测试的问题,可能会浪费时间去修改系统代码。而单元测试中,好保持逻辑的简单,因此尽量避免使用下面的逻辑控制代码。
  switch、if
  foreach、for、while
  一个单元测试应该是一系列的方法调用和断言,但是不应该包含控制流语句。
  只测试一个关注点
  在一个单元测试中验证多个关注点会使得测试代码变得复杂,但却没有价值。相反,我们应该在分开的、独立的单元中验证多余的关注点,这样才能发现真正导致失败的地方。
  0x05 单元测试的可维护性
  去除重复代码
  和系统中的重复代码一样,在单元测试中重复代码同样意味着测试对象某方面改变时要修改更多的测试代码。
  如果测试看上去都一样,仅仅是参数不同,那么我们完全可以使用参数化测试即使用[TestCase]特性将不同的数据作为参数传入测试方法。
  实施测试隔离
  所谓的测试隔离,指的是一个测试和其他的测试隔离,甚至不知道其他测试的存在,而只在自己的小世界中运行。
  将测试隔离的目的是防止测试之间的互相影响,常见的测试之间互相影响的情况可以总结如下:
  强制的测试顺序:测试要以某种顺序执行,后一个测试需要前面的测试结果,这种情况有可能会导致问题的原因是因为NUnit不能保证测试按照某种特定的顺序执行,因此通过的测试,明天可能不好用了
  隐藏的测试调用:测试调用其他测试
  共享状态被破坏:测试要共享状态,但是在一个测试完成之后没有重置状态,进而影响后面的测试
  0x06 单元测试的可读性
  正如概述中所说单元测试是一种无价的文档,它是展示方法或类如何使用的佳文档。因此,可读性这条要求的重要性便可见一斑。试想一下即便是几个月之后别的程序员都可以通过单元测试来理解一个系统的组成以及使用方法,并能够很快的理解他们要做的工作以及在哪里切入。
  单元测试命名
  在单元测试的结构中已经有过要求和介绍。参考那部分。
  单元测试中的变量命名
  通过合理的命名变量,可以提高可读性,使得阅读测试的人员可以尽快的理解你要验证的内容。
  还是看看上面的例子
  [Test]
  public void TakeDamage_BeAttacked_HpEqual()
  {
  //Arrange
  HpComp health = new HpComp();
  health.currentHp = 100;
  //Act
  health.TakeDamage(50);
  //Assert
  Assert.AreEqual(50f, health.currentHp);
  }
  这段代码中的断言使用了一个魔数50,但是这个数字并没有使用描述性的名字,因此我们无法尽快的知道这个数字预期的是什么。因此,我们尽可能不要直接使用数字和结果比较,而是使用一个有意义命名的变量来和结果进行比较。
  [Test]
  public void TakeDamage_BeAttacked_HpEqual()
  {
  HpComp health = new HpComp();
  health.currentHp = 100;
  health.TakeDamage(50);
  float leftHp = 50f;
  Assert.AreEqual(leftHp, health.currentHp);
  }
  0x07 在Untiy编辑器中写单元测试
  在Unity编辑器中编写单元测试用例时,使用的主要是Unity编辑器自带的单元测试模块,因此单元测试是基于NUnit框架的。
  这要求编写单元测试时,要引入NUnit.Framework命名空间,且单元测试类要加上[TestFixture]属性,单元测试方法要加上[Test]属性,并将测试用例的文件放在Editor文件夹下。
  测试用例的编写结构要遵循3A原则,即Arrange, Act, Assert。
  即先要设置测试环境,例如实例化测试类,为测试类的字段赋值。
  之后写测试的行为。
  后是判断是否通过测试。
  下面是一个例子:
using UnityEngine;
using System.Collections;
using NUnit.Framework;
[TestFixture]
public class HealthComponentTests
{
//测试伤害之后,血的值是否比0大
[Test]
public void TakeDamage_BeAttacked_BiggerZero()
{
//Arrange
UnMonoHealthClass health = new UnMonoHealthClass();
health.healthAmount = 50f;
//Act
health.TakeDamage(60f);
//Assert
Assert.GreaterOrEqual(health.healthAmount, 0);
}
}
  该例子是测试英雄受到伤害之后,血量是否会越界出现负值。
  测试框架会创建这个测试用例类,并且调用TakeDamage_BeAttacked_BiggerZero方法来和其交互,后使用Nunit的Assert类来断言是否通过测试。
  使用Editor Tests Runner开始单元测试:
  写完了单元测试用例之后,我们可以在Unity5.3.x的editor中开始单元测试了。如图所示:

  在这里,我们既可以跑单独的测试用例,也可以跑所有的测试用例,通过的是绿色标识,未通过的是红色标识。
  而在上面的一行,则是我们可以操作的部分:
  Run All:测试全部用例
  Run Selected:测试选中的用例
  Rerun Failed: 重新测试上一次未通过的测试用例
  搜索框:可以搜索用例
  种类过滤器:可以根据种类来筛选用例。种类需要在测试代码中使用CategoryAttribute来标识。
  测试结果筛选器:可以按照通过、失败以及忽略来筛选用例

  在这里我们还可以设置在编译前自动运行单元测试。
  使用命令行运行单元测试:
  除了能够在Editor中使用单元测试,我们自然更希望能够将单元测试也纳入自动集成的流水线中,因此有必要从U3D外部调用测试。不过好在U3D也提供了外部调用的方式,这样将单元测试也加入到我们的自动集成的流水线中是可行的。
  Unity3D 5.3.x版本中提供的命令行选项如下:
  runEditorTests  必须,运行editor test的选项
  editorTestsResultFile 用来保存测试结果
  editorTestsFilter 根据用例名称,来运行指定的用例
  editorTestsCategories 根据用例种类,来运行指定的用例
  editorTestsVerboseLog 打印更加详细的日志
  projectPath 工程目录
  所以在命令行中开启测试可以这样写:
  Unity -runEditorTests -projectPath /Users/fanyou/UnitTest -editorTestsResultFile  /Users/fanyou/UnitTest/test.xml -batchmode -quit
  0x08 后记
  以上便是关于在U3D中引入单元测试的一些思考,当然,游戏开发是否适合TDD,换言之是否要先写单元测试后实现功能是值得讨论的事情,但是单元测试本身是十分有必要在工程中使用的。在代码结构设计、日后的重构都会很有帮助。。

上一页12下一页
软件测试工具 | 联系我们 | 投诉建议 | 诚聘英才 | 申请使用列表 | 网站地图
沪ICP备07036474 2003-2017 版权所有 上海泽众软件科技有限公司 Shanghai ZeZhong Software Co.,Ltd