问说网 · 发表于 2014-12-02

文章13116 · 评论469

设计之道:使用NUnit测试和验证程序用例

字数5046 · 浏览 1102 · 评论 0

测试用例定义了一系列输入,同时也可以定义一系列输出,以验证被执行的某些程序特性没有引入任何Bug。测试可能会失败是因为产品里有Bug,但也可能是测试代码自身有Bug——可以称为false negative。相似的,测试能通过也有可能恰好是因为测试中有Bug,而非你的产品准确无误,我们称为false positive。[译注:意思是测试中有Bug,而产品中也有Bug,巧的是这两处的Bug恰好负负得正,抵消了Bug所带来的影响,反而使测试通过。]好的测试应该避免出现false negatives,且绝对不能包含false positives。另外,好的测试包不仅要测试正确的场景,也要测试错误的用例。

测试对象和其环境在测试用例被执行前总是保持相同的状态,因此一旦有必要就需重复执行并给出恒定的结果。当测试失败时,定位到错误的地方就能获得相关的信息。通过这些信息就能推断出问题能否被纠正。这就是为什么要求你对每个测试用例都要验证其合法性的原因所在。

现在我们为Quad例子写一些测试用例来实践上述的原理。在Visual Studio和NUnit 中打开Quad 项目,和起初打开Money 项目的方法一样——选择文件| 打开菜单,并定位到QuadApp.csproj。选择项目| 添加引用,找到文件nunit.framework.dll(在NUnit安装目录的bin子目录下),单击确定,添加引用nunit.framework。

在Visual Studio中创建QuadTest类:选择项目| 添加类,输入名字QuadTest.cs,然后单击“打开”。输入下列代码到QuadTest中:

using System;
using NUnit.Framework;
namespace QuadApp{
    [TestFixture]
    public class QuadTest{
        [TestFixtureSetUp]
        public void DoTestSuiteSetUp(){
            Console.WriteLine("TestSuiteSetUp");
        }
        [TestFixtureTearDown]
        public void DoTestSuiteTearDown(){
            Console.WriteLine("TestSuiteTearDown");
        }
        [TearDown]
        public void DoTestCaseTearDown(){
            Console.WriteLine("TestCaseTearDown");
        }
        [SetUp]
        public void DoTestCaseSetup(){
            Console.WriteLine("TestCaseSetup");
        }
        [Test]
        public void Area(){
            Quad q = new Quad();
            Assert.IsTrue( q.Area(2, 3) == 6);
            Console.WriteLine("Area");
        }
        [Test]
        public void Perimeter(){
            Quad q = new Quad();
            Assert.IsTrue( q.Perimeter(2, 3) == 10);
            Console.WriteLine("Perimeter");
        }
    }
}

编译后,在NUnit 中运行测试。单击NUnit 右侧的Console.Out 标签,可以查看执行的顺序。

现在你可以看到,不同的Setup 和TearDown 方法是怎样允许你控制测试环境,从而保证你的测试用例能根据需要而重复执行并给出恒定的结果。这些方法将帮助你确保运行一个测试用例而不会影响到另外的用例(测试用例独立性test case isolation)。测试独立性的最佳证明就是具备以任意顺序运行测试用例的能力。试着改变一下测试用例的顺序,以确认他们是否具有独立性。

测试套件和测试包

一个测试套件是一个或多个测试用例共享的一个对象,这些测试用例都与测试对象的初始化或提供相应资源有关。在NUnit 的术语中,测试套件是一个具备特性(attribute)[TestFixture]的类,并提供下列方法:

  • [Test]方法形成不同的测试用例。这些测试样例应该是测试的核心操作,并且与其它测试无关。
  • [SetUp]和[TearDown]方法提供测试用例间的环境设置。它们分别在测试开始前和结束后执行。
  • [TestFixtureSetUp]和[TestFixtureTearDown]方法要求对象被测试用例共享。它们分别在测试套件开始和结束时执行。

你希望运行测试的任意对象都可以在[TestFixture]类(被所有测试用例共享)里创建实例变量,也可以在方法(对于单个测试用例中是私有的)中创建局部变量,这就是为什么要描述为测试套件(test fixture)的原因。然而,你也可以将NUnit 的[TestFixture]看作是组织测试包的一种方法,它将测试包组织为每个类,形成treeview的每一个分支。

既然你已经能够为自己的项目创建测试包,并且通过NUnit来运行它们,那么就让我们开始TDD之旅吧。当然,首先还要看看我们在开发工作中可能会遇到的问题。

在现实世界中使用TDD

在使用TDD时,首先要考虑的是:既然商业产品是设计用来组织你的程序的,那么产品代码就应该很容易从代码中分离出来,以用来测试。在产品开发期间,你可以运行你的测试程序,而为了发布产品,你仍然能够轻易地将其从代码中移除。

你可能会遇到的另一个难题就是测试GUI 应用程序的困难,因为GUI 应用程序是被鼠标和键盘输入而驱动的。举例来说,你应该怎样写测试程序来激活用户点击dropdown list控件的事件呢?如果用户选择的是国名列表,那么又该怎样验证选中的内容呢?

解决这些问题的方案就是:将代码根据编译、测试和部署分类为不同的组件。举例来说,我们不应创建一个作为主应用程序的Quad类,生成相同的可执行文件;而应该允许这个类能够被包含在一个单独的库文件(.dll)中。[译注:实质就是将Quad 以类库的形式创建,而非应用程序。] 这样我们就可以开发单独的测试程序和域程序作为各自的可执行文件(.exe),并共享同一个包含Quad类的公共库文件(.dll)。注意,如果你仅仅创建了库,NUnit和你的测试包会被当作接口使用,而不是所需的单独的业务外观(separate harness)。

记住这个原则:“使主程序尽量简单,而将复杂的业务作为类放到库中”。它将帮助你解决测试GUI应用程序的问题。TDD的其中一个原则就是你不能测试第三方代码,因此你没有必要测试GUI 框架类,虽然当框架相对简单的时候,有时会用来测试接口。这意味着你可以捕捉类里的用户事件,因为你知道这个类是怎么工作的;然后再将其传递到类里进行处理。此外,你也可以将测试程序和域程序从单独的可执行文件中分离出来,而该执行文件则共享了你竭尽心力开发的公共库。

让我们看看怎样实际运用TDD来开发包含有Combobox控件的简单窗体应用程序。

本文系作者 问说网 授权问说网发表,并经问说网编辑,转载请注明出处和 本文链接

问说网手机版

躺着 站着 跪着轻松访问

更多 热门话题

APP界面

APP界面

文章 41517

APP欣赏

APP欣赏

文章 41450

APP手机界面

APP手机界面

文章 41440

图片素材

图片素材

文章 29463

高清图片

高清图片

文章 26225

更多 推荐作者

问说网

文章 13116

GallupDarnell

文章 117

GibbonRoy

文章 119

StuartKelly

文章 114

YeatesGeoffrey

文章 113

发布评论

顶部 反馈 评论 底部

意见反馈

感谢您对问说网的支持,提出您在使用过程中遇到的问题或宝贵建议,您的反馈对我们产品的完善有很大帮助。

您的反馈我们已收到!

感谢您提供的宝贵意见,我们会在1-2个工作日,通过您留下的联系方式将处理结果反馈给您!