《XNA高级编程:Xbox 360和Windows》3-4

3.4 XNA 中的单元测试
 

     在进一步学习辅助类之前,本节先来简单地讨论一下单元测试。上一章您已经学习了静态单元测试( static unit test ),静态单元测试非常适用于快速检测输出结果、测试物理特性和控制器以及快速构建游戏。但是辅助类和组件不需要用户输入,这就要求您自己来处理它们。这是没有意义的,因为单元测试主要是为了改进应用程序的可维护性,并确保尽可能少地出现错误。例如,可以使用下面的代码来测试 Log 类是否可以工作:

FileHelper.DeleteFile(Log.LogFilename);
Log.Write(
" New log entry " );

提示:这段代码只能在 Log 类内部执行,因为 Log.LogFilename 是私有的。

     现在,您可以查看一下应用程序的目录中是否有日志文件,并且其中包含“ New log entry ”信息。不过,自己反反复复地检查文件内容是很麻烦的。不要把所有错误信息都记录下来,您只需记录下不是特别重要的警告信息(比如用户没有连接到 Internet ),而对于严重的错误要抛出异常(比如找不到 texture shader 不可用等等)。当问题变得越来越多,测试变得更加复杂并且需要漫长的检测过程,尤其应当这么做。您可以用单元测试来进行错误检测,这样就不用亲自去做了,也可以让它们自动地被执行,而不是像使用静态单元测试要亲自从 Program 类中去调用。

NUnit和TestDriven.Net

     要达到上述目的您可以使用流行的 NUnit Framework ,可以在这里下载: http://www.nunit.org

     另外,如果您使用的是 Visual Studio 2005 Professional 或更高版本,也可以使用 TestDrive.Net ,可以从 http://www.testdriven.net 得到。它支持很多非常好的特性,直接使用热键和弹出菜单就可以进行测试,非常简单。 VC# Express XNA Studio Express 不支持 TestDriven.Net (一年前倒是支持的,但由于 Microsoft 希望开发者使用 Professional 版本,所以 Express 版本就不再支持这个插件)。关于在 Visual Studio 2005 中如何使用 XNA 可以参见第一章的相关内容,可以在 XNA Studio Express 中使用一个临时项目来处理内容素材,因为在 Visual Studio 2005 中不支持。

     不管安装哪个版本,只要把动态库 NUnit.Framework.dll 添加到项目中就可以了(右键单击项目添加一个新的引用,在打开的窗口中第一个标签页显示的是全局程序集缓存—— Global Assembly Cache ,如果这里找不到 NUnit.Framework.dll ,可以切换到“浏览”标签页定位到 NUnit 的安装目录中找到它)。现在您可以添加下面的指令:

#if  DEBUG
using  NUnit.Framework;
#endif

我通常把这段代码放在 using 指令区域的顶部,而且它之所以只在调试模式下使用,是因为只有在调式模式下才会使用单元测试,对于最终发布的游戏您也不想要这些额外的 NUnit.Framework.dll 和所有的测试代码,因为游戏不需要这些。

     作为一个例子,您可以看看 StringHelper 类中使用的第一个单元测试,它主要检测 IsInList 方法是否按预期工作:

[TestFixture]
public   class  StringHelperTests
{
    
///   <summary>
    
///  Test IsInList
    
///   </summary>
    [Test]
    
public   void  TestIsInList()
    {
        Assert.IsTrue(IsInList(
" whats " ,
            
new   string [] {  " hi " " whats " " up? "  },  false ));
        Assert.IsFalse(IsInList(
" no way " ,
            
new   string [] {  " omg " " no no " " there is no way! "  },  false ));
    } 
//  TestIsInList()

其中 Assert NUnit 框架中的一个辅助类,它包含一些检查返回值是否正确的方法。如果返回值不正确,就会抛出一个异常,您可以立即看到是哪行出了错。例如上述代码中, Assert.IsTrue 检查 IsInList 方法的返回值是否是 true ,如果是 false ,就抛出一个异常。其中第一个测试的字符串数组中包含了字符串“ whats ”,返回值就是 true ,那么这个测试就通过了。第二个测试是否存在“ no way ”,而字符串数组中没有,那么返回值就是 false 。请注意:虽然“ there is no way! ”中包含了“ no way ”,但此处测试的不是 Contains 方法(虽然 StringHelper 类中的确存在这个方法)。只有当字符串列表中完整包含一个字符串的时候, IsInList 方法才返回 true

开始单元测试

     您可以使用 TestDriven.Net 运行测试,单击鼠标右键并选择“ Run Test ”(如图 3-6 所示),如果您没有或者无法使用 TestDriven.Net ,也可以使用 NUnit 程序。您也可以在 TestDriven.Net 中使用相同的方式来做静态单元测试,但 NUnit GUI 不支持静态单元测试。所以,我在 Program.cs (或者是后面项目中使用的 UnitTesting.cs 类)中添加单元测试来支持所有用户和 XNA Studio Express TestDriven.Net 可以用来进行动态和静态单元测试,但 2.0 版本之后您必须把 [Test] 特性从静态单元测试中去掉,否则无法正常工作(本书中的静态单元测试都没有使用 [Test] 特性)。
《XNA高级编程:Xbox 360和Windows》3-4_第1张图片
如图 3-6

     此时运行测试不会产生任何错误,但如果把“whats”改成“whats up”,第一个Assert测试就会失败,并得到下面的结果:

TestCase  ' M:XnaBreakout.Helpers.StringHelper.StringHelperTests.TestIsInList '  failed:
  NUnit.Framework.AssertionException
  at NUnit.Framework.Assert.DoAssert(IAsserter asserter)
  at NUnit.Framework.Assert.IsTrue(Boolean condition, String message, Object[] args)
  at NUnit.Framework.Assert.IsTrue(Boolean condition)
  C:\code\XnaRacer\Helpers\StringHelper.cs(
1387 , 0 ): at
    XnaBreakout.Helpers.StringHelper.StringHelperTests.TestIsInList()

0  passed,  1  failed,  0  skipped,  took  0 , 48  seconds.

它会告诉您错误在哪儿(双击错误信息会自动跳转到1387行)以及应该如何修改。如果使用NUnit,错误会更明显(如图3-7所示):
《XNA高级编程:Xbox 360和Windows》3-4_第2张图片
3-7

     NUnit GUI是一个非常好的工具,一次可以运行多个单元测试,并很快看到哪些出了错,然后深入源代码追查错误。您可以使用菜单“FileLoad”来选择程序,或者直接把任何.Net开发的.exe.dll文件拖放到它上面,然后就可以看到程序集里面的所有单元测试,点击“Run”就可以测试了。它虽然很好使,但通常我在编码或者测试的时候不想切换出开发环境,所以我更喜欢TestDriven.Net并一直使用它。要修复这个错误,您只需把“whats up”改回“whats”,这样所有的测试就可以通过了。

黄金法则

     这里我不准备写太多关于单元测试的东西,因为第二章已经讨论了最基本的规则,它们也适用于动态单元测试。当您开始写第一个单元测试的时候,要紧记下面的这些规则:
  • 仔细分析您的问题,然后把它们分解成容易处理的小问题
  • 先写测试,不要考虑具体怎么实现,您认为最终的游戏代码会是什么样,就怎么写
  • 测试要尽可能地多。例如,前面的TestIsInList不仅测试了成功调用IsInList,还测试了对它的失败调用。单元测试是要花些时间,但千万别超过50%——用不着为只有两行代码的方法进行多达30次的测试
  • 从此刻开始测试要不间断地进行,即使您认为它没有多大意义。这会让您意识到该做什么,还有多少东西要实现。开始的时候,测试可能都无法通过编译,因为您还没有实现任何东西。当实现了一些空的方法之后,测试会失败,因为并没有做实际的事情。最后当所有东西都能正常工作了,您就会感觉好多了
  • 即使您不经常测试静态单元测试,但每次您编译代码的时候动态单元测试都被执行(如果它们都运行得足够快)。每天一次或者每周一次运行所有单元测试,来确保对代码做的最新修改不会导致新的错误。

你可能感兴趣的:(windows)