技术开发 频道

使用Visual Studio进行单元测试

    这些自动化单元测试用什么运行?
    正如前面提到的,xUnit 框架将“测试运行器”的概念定义为应用程序负责:(a) 执行单元测试;(b) 报告测试结果。对于本文,包含 Visual Studio 2005 Team System (VSTS) 的 Unit Testing 引擎作为我们的“测试运行器”。图 2 表示 BankAccountTests.cs 类的执行结果。

    

    图 2. 测试结果窗格:单元测试执行结果

    Microsoft Visual Studio 2005 使用源项目的代码模型动态填充该视图。它基于该源代码中的自定义属性动态发现有关该测试套件的信息。表 4 表示最常见的单元测试属性(以及执行的次序)。

    表 4. 常见单元测试属性
    属性 描述
    TestClass()
    该属性表示一个测试装置。
 
    TestMethod()
    该属性表示一个测试用例。
 
    AssemblyInitialize()
    在执行为执行选择的第一个 TestClass() 中的第一个 TestMethod() 之前,执行带有该属性的方法。
 
    ClassInitialize()
    带有该属性的方法在执行第一个测试之前调用。
 
    TestInitialize()
    带有该属性的方法在执行每个 TestMethod() 之前调用。
 
    TestCleanup()
    带有该属性的方法在执行每个 TestMethod() 之后调用。
 
    ClassCleanup()
    带有该属性的方法在执行 ALL 测试之后调用。
 
    AssemblyCleanup()
    在执行为执行选择的第一个 TestClass() 中的第一个 TestMethod() 之后,执行带有该属性的方法。
 
    Description()
    提供关于给定 TestMethod() 的描述。
 
    Ignore()
    由于某种原因忽略 TestMethod() 或 TestClass()。
 
    ExpectedException()
    当测试特定异常时,如果使用该属性指定的异常不是从实现代码引发,则测试不会失败。
 

    我编写什么类型的测试?
    一个方法及其相关测试之间很难有一对一关系。编写自动化单元测试需要开发人员“进行全面思考”,并了解关于对象的所有内容 — 它将如何消耗、使用、处理,以及在任何情况下如何起到积极、消极、非决定性作用。

    例如,请考虑一个用于针对数据库中 Customer 项执行 CRUD(创建、检索、更新、删除)功能的典型对象方法。对于该对象的 Load() 方法,要针对以下方案编写测试:

    ? 构造函数测试 — 确保对象正确加载,带有正确的信息。
 
    ? PositiveLoadScalarTest — 测试数据库中一个 Customer 的成功加载。
 
    ? NegativeLoadScalarTest — 测试一个 Customer 的失败加载,即该 Customer 不在数据库中。
 
    ? PositiveLoadTest — 基于已知数据测试 Customer 的成功加载。
 
    ? NegativeLoadTest — 测试数据库中不存在的 Customer 的失败加载。
 
    ? NegativeValidationTest — 确保验证逻辑正确工作。
 

    这些只是自动化单元测试套件许多用法中的一部分。我曾经听说一个小团队使用单元测试查看针对其组件的已知安全攻击。从宏观的角度来看,单元测试应该明确保证组件的正常使用。具有丰富的测试集将使团队确信您已经准确实现了既定的目标:编写有效的软件。无论自信源自哪里 — 这就是您需要编写的测试。

    您测试什么?
    从本质上看,这些自动化单元测试非常低级。它们旨在测试下至构造函数、方法调用的对象,甚至是对象上的属性。

    关于“公共对私有”的主题在单元测试派系中引发了许多争论。许多人认为单元测试只应该测试对象的公共接口。其他人认为应该测试每个调用 — 包括内部私有方法。VSTS 支持两个单元测试级别。VSTS 通过使用私有访问器或包装类支持私有测试,后者提供基于“私有”方法和属性生成单元测试的功能。

    为什么生成代码?
    阅读上面的列表后,您可能会想起前面项目的单个对象,并思考:“如果我用“这些”对象进行该操作,就需要编写大量代码!”请考虑开发人员仍编写“单元测试”代码的事实 — 只在不同的窗体(例如,前面提到的 WinForms 示例)上进行。此外,具有可自行记录、可重用的实现示例带来的好处远大于生成更多代码所带来的麻烦。最后,在单元测试中设计更多的环节已证明可以减少质量保证环节中的故障。

    正如前面所提到的,代码生成是“软件创建软件”的过程。基于可重复的过程创建代码是理想的。例如,一些使用代码生成的较好示例包括:脚本数据、创建表示实体及其在储存库(数据库 CRUD)中存在的对象,或者创建适用于数据维护的 UI 控件。使用代码生成的好处包括:

    ? 节省时间 — 为什么花几小时/天/周创建一些在几秒/分钟内就可以创建的内容?
 
    ? 强制标准/约定— 对于强制您的标准和命名约定而言,没有什么比消除开发中的人员因素并依赖基于“您的”规则的可重复过程更好的了。
 
    ? 测试私有方法的功能 — 正如本文前面提到的,Unit Testing Framework 提供使用“私有访问器”类测试私有方法的功能。该代码生成引擎创建与这些访问器类相关的所有“基础代码”。
 
    ? 获取现有组件的信息 — 搜索另一个开发人员的组件吗?基于这些组件生成代码可能提供关于该实现以及该对象接口的简明示例。此外,进行设计并在编码之前“清除”其对象的公共接口(例如,通过使用 VS 2005 类设计器)的开发人员将极大地受益于代码生成引擎。
 
    正如您所预期的,自动化单元测试属于“优秀代码生成候选者”。在现有组件中指出一些内容并针对这些自动单元测试生成初始代码难道不是很理想吗?而且不只是保留单元测试框架,还会围绕对象的公共接口生成实现示例吗?将来的 Visual Studio 2005 Team System 用户将拥有该功能以及更多功能!

0
相关文章