65.9K
CodeProject 正在变化。 阅读更多。
Home

NUnit 和 TDD 入门

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.81/5 (39投票s)

2011 年 2 月 25 日

CPOL

7分钟阅读

viewsIcon

118936

本文将介绍 NUnit 和 TDD

引言

NUnit 是 .NET 应用程序的优秀单元测试工具。在本文中,我将解释 NUnit 的基础知识,并指导您逐步创建示例单元测试。但是,如果您正在寻找单元测试,您还应该了解测试驱动开发的概念。反之亦然,如果 TDD 是您的目标,您也必须浏览 NUnit。为了真正发挥单元测试的优势,TDD 是最好的方法。因此,本文还涵盖了 TDD 的概念。

什么是单元测试

单元测试是一种开发过程,程序员在开发软件时创建测试。这些测试是简单简短的测试,用于测试其代码的特定单元或模块的功能,例如类或函数。单元测试的一般行为是它向类发送预定义的消息并验证响应。是的,您没听错!开发者负责单元测试,这意味着您需要编写额外的代码!大约 20% 的额外工作!等等,好处远不止这些。

让我们先了解 NUnit

NUnit 是一个单元测试框架,使用 C# 开发。它使用基于属性的编程模型。它提供了漂亮的、干净的 GUI 以及用于测试程序集的控制台应用程序。稍后我们将详细介绍。首先,让我们创建一个示例测试。

这里我以 `Person` 类为例。所以在项目中创建一个如下的类。

public class Person
    {
        public string Lname
        { get; set; }

        public string Fname
        { get; set; }

        public string Mname
        { get; set; }

        public string Address
        { get; set; }

        public string Email
        { get; set; }

        public string Phone
        { get; set; }


        public string GetFullName()
        {
            if(string.IsNullOrEmpty(Lname))
                throw new MissingFieldException("Lname is empty");

            return string.Format("{0} {1} {2}",Fname,Mname,Lname);
        }
    }

步骤 1:从已安装的 nunit 应用程序的 bin 目录中添加对 `nunit.framework.dll` 的引用。

步骤 2:创建一个类来编写您的测试。假设您想测试一个“Person”类。将您的测试类命名为 `TestPerson`。最佳实践:使用单独的类库。为应用程序中的每个类使用单独的类进行测试。

步骤 3:为类添加 `Attribute`。

测试类应具有“Test Fixture”属性。

让我们理解这个属性

[TestFixture]
  • 将要包含测试代码的类应为此属性打上标签。
  • 当您将此属性附加到项目中的一个类时,Test Runner 应用程序将扫描它以查找测试方法。
  • 类必须有一个 `public` 默认构造函数。

代码片段

[TestFixture]
public class TestPerson 

步骤 4:编写测试

创建一个名为 `testFullName()` 的方法。此方法将包含测试“GetFullName()”方法的代码。

此方法应标记为 `Attribute [Test]`。任何包含执行测试的实际代码的方法都应标记为该属性。签名必须是:`public void ()`。

现在,在此方法中实现以下代码

using NUnit.Framework;

namespace NunitExample
{
    [TestFixture]
    public class TestPerson
    {

        [Test]
        public void TestFullName()
        {
            
            Person person = new Person ();
    		 person.lname = "Doe";
            person.mname = "Roe";
            person.fname = "John";

            string actual = person.GetFullName();
            string expected = "John Roe Doe";
            Assert.AreEqual(expected, actual,
		”The GetFullName returned a different Value”);
        }
    }
}

让我们来理解这段代码:测试方法为 `Person` 类的属性分配固定值。现在我们要检查 `GetFullName()` 是否正常工作。因此,我们将返回的值与我们预期的值进行比较。如果匹配,测试通过,否则测试失败。

为了比较结果,我们使用 Nunit Framework 的“Assert”类。断言是任何单元测试框架的核心。在这里,我们使用“AreEqual”方法来比较两个结果。

如您所见,`AreEqual()` 需要 3 个参数。第三个是可选的。前两个是我们想要比较的值,第三个是在测试失败时显示的帮助信息。

`AreEqual` 方法有许多重载,因此您可以比较不同的数据类型。

`Assert` 类有许多不同的方法,支持集合、异常、目录、文件等。我们在这里不一一介绍。您可以在官方 Nunit 网站上轻松了解所有这些。

步骤 5:运行代码。

编译您的代码。

让我们使用 GUI 来运行代码。打开 GUI,从文件菜单中选择“New project”。为新项目保存一个名称。

然后转到项目菜单,选择“Add Assembly”,然后选择包含您的测试类的程序集。您将看到测试类及其测试方法显示在窗口的左侧面板中。

选择 `PersonTest` 类,然后单击 Run。此类的所有测试都将被运行。

2-25-2011_11-31-13_AM.png

当您看到所有内容都变成绿色时,就是庆祝的时候了!您所有的测试都已通过。当您仔细查看屏幕时,您会发现它显示了各种参数,例如哪些测试已通过、失败、执行测试所花费的时间等。

现在,在测试方法中更改上面的代码,以获得无效结果。例如:

Assert.AreEqual(“abcd”, “actual” ,”The GetFullName returned a different Value”). 

重新构建程序集,然后在 Nunit GUI 中,单击 File->Reload tests。

现在,再次运行,看看测试失败时屏幕是什么样的。

failed.png

看到红色,它还显示了您指定的帮助信息。

好了,现在您知道如何运行和编写单元测试代码了。让我们快速浏览一下其他一些重要且非常有用的属性。

一些重要的属性

Setup 属性

[SetUp]
public void init()
{
    Person person = new Person();
}

如您在代码中看到的,`Setup` 属性应用于任何初始化类型的函数。在执行测试之前必须执行的任何代码都可以放在用 `[Setup]` 标记的函数中。这使您不必在每个测试中重复这些代码行。请仔细注意,这段代码是在每个测试之前执行的。

Tear Down

此属性与 `Setup` 属性完全相反。这段代码是在每个代码执行之后执行的。例如,在此方法中,您可以编写代码来关闭任何 `FileSystem` 对象或数据库连接。

Exception Expected

有时,我们可能希望在特定场景下测试某个方法是否会抛出异常。我们可以使用此属性来测试这一点。
示例

[Test]
[ExpectedException(typeof(MissingFieldException))]
public void TestFullNameForException()
{
    Person person = new Person();
    person.Lname = "";
    person.Mname = "Roe";
    person.Fname = "John";

    string actual = person.GetFullName();
}

这段代码没有任何 `Assert` 语句。如果调用“GetFullName()”时抛出了“MissingFieldException”,则此测试将通过。如果没有抛出异常,测试将失败。

Ignore

您想暂时跳过的任何测试都可以用此属性标记。

一些显而易见的优势

  • 害怕改变?不再是了!
  • 代码中的任何更改都会立即产生已知的影响。
  • “测试不是你“做”的东西,而是你“写”并运行一次、两次、三次等的东西。”

最佳实践:可以使用 Nant 等工具进行自动计划构建。每次构建时,Nant 都会运行 Nunit 测试,并将结果生成到 XML 文件中!

测试驱动开发 – TDD

测试驱动的概念可以简单地用以下两句话来概括。

“第一步是快速添加一个测试,基本上只需要足够的代码让它失败。接下来运行您的测试,通常是完整的测试套件,尽管为了速度,您可能会决定只运行一部分,以确保新测试确实失败。然后更新您的功能代码以使其通过新测试。第四步是再次运行您的测试。如果失败,您需要更新您的功能代码并重新测试。一旦测试通过,下一步就是重新开始(您可能首先需要重构设计中任何重复的部分,将 TFD 转换为 TDD)”。

tdd_-_Copy.png

这是一个迭代过程,您会一直进行,直到所有测试都通过。

TDD 的一些已证实的优势

  • 单元测试在开发周期的早期发现问题
  • 自动化的单元测试套件在两个维度上监视您的代码:时间和空间
  • 开发者将不再害怕更改现有代码
  • 开发过程变得更加灵活
  • 提高您项目的“卡车因子”(Truck Factor)
  • 手动测试的需求
  • 软件开发将变得更加可预测和可重复
  • 更短的开发周期

对阅读本文的项目经理来说,值得关注的一点

TDDchange_-_Copy.png

绿色图表显示了 TDD 周期,而红色图表显示了传统的先编码后测试的开发方法。

注意

在结束本文之前,还有一个小小的警告

  • 所有测试通过并不意味着软件已准备好交付。
  • 单元测试取代传统的系统测试。

即将推出

一旦您开始使用 TDD,您可能会遇到一些挑战。您可能需要实现适当的设计模式和模拟(mocking)才能成功实现单元测试和 TDD。我的下一篇文章将涵盖这些内容。请耐心等待,它很快就会到来。

历史

  • 2011 年 2 月 24 日:初始发布
© . All rights reserved.