Getting Started with NUnit

如果你打算开始学习,到下载页面选择一个NUnit版本。安装页面包含了安装说明。

开始NUnit阅读Quick Start页面。验证了一个C#银行应用程序的开发过程。查看Samples部分例子,包括了VB.NET、J#以及托管C++。

使用哪种形式?

  NUnit有两种不同方式来测试用例。

    A.控制台:NUnit-Console.exe,可以最快的使用,但是不能进行交互。

    B.GUI:NUnit.exe,是一个Windows窗体应用程序提供可视化的界面,同时能够可选择性是运行测试。

NUnit Quick Start

Note:本页面是基于原来早期NUnit版本的QuickStart.doc文件。但它不是测试驱动开发的好例子。然而,我们仍然保留这个文档是因为它说明了使用NUnit的基础知识。在将来的版本中我们会对它进行更新。

现在我们从一个简单的例子开始,源代码在这里。假设我们开发一个银行应用程序,我们有有一个基本的域类-Account。这个类支持存款、取款、转账业务。代码如下:

namespace Bank
{
  public class Account
  {
    private decimal balance;
     
    public void Deposit(decimal amount)
    {
      balance += amount;
    }
 
    public void Withdraw(decimal amount)
    {
      balance -= amount;
    }
 
    public void TransferFunds(Account destination, decimal amount)
    {
    }
 
    public decimal Balance
    {
      get { return balance; }
    }
  }
}

现在我们为这个类写一个简单的测试-AccountTest。我们测试的第一个方法是TransferFunds。

namespace Bank
{
  using NUnit.Framework;
 
  [TestFixture]
  public class AccountTest
  {
    [Test]
    public void TransferFunds()
    {
      Account source = new Account();
      source.Deposit(200m);
       
      Account destination = new Account();
      destination.Deposit(150m);
 
      source.TransferFunds(destination, 100m);
       
      Assert.AreEqual(250m, destination.Balance);
      Assert.AreEqual(100m, source.Balance);
    }
  }
}

这个类首先需要注意到是它包含一个TestFixture特性(Attribute翻译为特性),通过这种方式指出这个类包含测试代码(特性可以被继承)。这个类是public修饰的并且它的父类没有限制。这个类也有一个默认构造函数。

这个类唯一的方法--TransferFunds,它有一个Test特性来关联它,它指出这个方法是一个测试方法。测试方法返回类型必须为void而且没有参数。在测试方法中我们初始化需要的测试的对象,执行测试业务逻辑并检查业务对象的状态。Assert类定义了用户检查后知条件的方法集合,在我们的例子中使用AreEqual方法确保转账后两个账户收支平衡。Assert有几个重载版本:第一个参数是期望值,第二个参数是实际值。

编译并运行这个例子。假设你已经编译代码为bank.dll。启动NUnit GUI(安装包已经在你的电脑桌面上和"Program Files"文件夹创建了一个快捷方式),启动之后选择File->Open菜单选项,浏览bank.dll并在"Open"对话框中选择它。当加载bank.dll是你会在左边控制板看见一个树结构的测试用例,在右边面板有一个状态集合。点击运行按钮,测试树中状态条和TransferFunds节点会变红--测试失败了。 “Errors and Failures”面板会显示一下信息:

  

TransferFunds : expected <250> but was <150>

在右下角堆栈信息面板会显示测试代码失败的地方:

at bank.AccountTest.TransferFunds() in C:\nunit\BankSampleTests\AccountTest.cs:line 19

以上是预期结果。这个用例失败是因为我们还没有实现TransferFunds方法。现在我们对它进行完善。不要关闭GUI并回到IDE修改源码,把TransferFunds改变如下

public void TransferFunds(Account destination, decimal amount)
{
  destination.Deposit(amount);
  Withdraw(amount);
}

现在编译代码,重新点击运行按钮,状态栏和测试树会变绿。注意GUI是怎么重新自动加载程序集,整个过程我们会保持GUI与IDE中代码为打开状态,并且编写更多的代码。

现在添加一些错误来检查Account类。我们为账户添加最小余额来保证银行能够在你账户上收取费用。现在在Account类上添加最小余额属性:

private decimal minimumBalance = 10m;
 
public decimal MinimumBalance
{
  get{ return minimumBalance; }
}

我们用一个异常来指出透支:

namespace Bank
{
  using System;
   
  public class InsufficientFundsException : ApplicationException
  {
  }
}

在AccountTest类添加一个新的测试方法:

[Test]
[ExpectedException(typeof(InsufficientFundsException))]
public void TransferWithInsufficientFunds()
{
  Account source = new Account();
  source.Deposit(200m);
     
  Account destination = new Account();
  destination.Deposit(150m);
     
  source.TransferFunds(destination, 300m);
}

这个测试方法添加了Test特性之外还添加了ExpectedException特性,通过这种方式指出测试代码执行的时候预期抛出一个异常类型实例。如果没有抛出异常那么测试失败。编译代码然后回到GUI。在编译代码的时候GUI会变灰色并且折叠测试树仿佛测试没有运行(当测试树结构改变时,GUI观察到测试程序集的修改并且进行更新就像添加了一个新的测试用例)。点击运行按钮,状态栏会再次变红并会得到如下失败信息:

TransferWithInsufficentFunds : InsufficientFundsException was expected

再次修正Account代码,将TransferFunds修改为如下:

public void TransferFunds(Account destination, decimal amount)
{
  destination.Deposit(amount);
     
  if(balance-amount < minimumBalance)
    throw new InsufficientFundsException();
         
  Withdraw(amount);
}

编译并运行测试,状态栏百年绿,测试成功。但是看看我们刚才写的代码我们会看到账户在每个失败的转账操作中都支出了钱。写一个测试来确认怀疑,添加如下测试方法:

[Test]
public void TransferWithInsufficientFundsAtomicity()
{
  Account source = new Account();
  source.Deposit(200m);
 
  Account destination = new Account();
  destination.Deposit(150m);
     
  try
  {
    source.TransferFunds(destination, 300m);
  }
  catch(InsufficientFundsException expected)
  {
  }
 
  Assert.AreEqual(200m, source.Balance);
  Assert.AreEqual(150m, destination.Balance);
}

我们测试业务方法的事物属性--所有的方法同时成功或者同时失败。

编译并运行--状态栏变红。我们从源账户中支取$300并且成功执行,但是目的账户显示$450.怎么来修复这个问题。我们可以在更新前查询源账户并验证余额是否小于最小余额:

public void TransferFunds(Account destination, decimal amount)
{
  if(balance-amount < minimumBalance)
    throw new InsufficientFundsException();
     
  destination.Deposit(amount);
 
  Withdraw(amount);
}

如果Withdraw方法抛出另外一个异常会怎么样呢?我们可以在catch块进行恢复事物或者依靠事物管理器来恢复对象状态。我们会在某些时候回答这些问题但不是现在。但在这期间我们怎么处理失败的测试呢,移除吗?更好的方式是暂时的忽略它并添加如下的方法到测试方法:

[Test]
[Ignore("Decide how to implement transaction management")]
public void TransferWithInsufficientFundsAtomicity()
{
  // code is the same
}

编译并运行--状态栏变黄。点击“Tests Not Run” 选项卡,你会看见在列表中看见bank.AccountTest.TransferWithInsufficientFundsAtomicity()被忽略的原因。

仔细看我们的测试代码会发现进行了一些重构。所有的测试方法都共享一系列测试对象。我们提炼出初始化代码到一个Setup方法,在所有测试中进行重用。测试类重构后如下:

namespace Bank
{
  using NUnit.Framework;
 
  [TestFixture]
  public class AccountTest
  {
    Account source;
    Account destination;
 
    [SetUp]
    public void Init()
    {
      source = new Account();
      source.Deposit(200m);
       
      destination = new Account();
      destination.Deposit(150m);
    }
 
    [Test]
    public void TransferFunds()
    {
      source.TransferFunds(destination, 100m);
       
      Assert.AreEqual(250m, destination.Balance);
      Assert.AreEqual(100m, source.Balance);
    }
 
    [Test]
    [ExpectedException(typeof(InsufficientFundsException))]
    public void TransferWithInsufficientFunds()
    {
      source.TransferFunds(destination, 300m);
    }
 
    [Test]
    [Ignore("Decide how to implement transaction management")]
    public void TransferWithInsufficientFundsAtomicity()
    {
      try
      {
        source.TransferFunds(destination, 300m);
      }
      catch(InsufficientFundsException expected)
      {
      }
 
      Assert.AreEqual(200m, source.Balance);
      Assert.AreEqual(150m, destination.Balance);
    }
  }
}

注意初始化方法会有共享初始化代码。它返回值为void,没有实参,并被使用了Setup特性。编译并运行,状态栏变黄了。

[翻译]NUnit--Getting Started(二)的更多相关文章

  1. Gradle 1.12用户指南翻译——第五十二章. Maven 插件

    本文由CSDN博客貌似掉线翻译,其他章节的翻译请参见:http://blog.csdn.net/column/details/gradle-translation.html翻译项目请关注Github上 ...

  2. Gradle 1.12用户指南翻译——第三十二章. JDepend 插件

    本文由CSDN博客万一博主翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Githu ...

  3. Gradle 1.12用户指南翻译——第四十二章. Announce插件

    本文由CSDN博客万一博主翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Githu ...

  4. 深度学习论文翻译解析(十二):Fast R-CNN

    论文标题:Fast R-CNN 论文作者:Ross Girshick 论文地址:https://www.cv-foundation.org/openaccess/content_iccv_2015/p ...

  5. 翻译qmake文档(二) Getting Started

    翻译qmake文档 目录 原英文文档: http://qt-project.org/doc/qt-5/qmake-tutorial.html         本教程教讲授qmake基础知识.这个手册里 ...

  6. 中文翻译:pjsip教程(二)之ICE穿越打洞:Interactive Connectivity Establishment简介

    1:pjsip教程(一)之PJNATH简介 2:pjsip教程(二)之ICE穿越打洞:Interactive Connectivity Establishment简介 3:pjsip教程(三)之ICE ...

  7. 翻译qmake文档 目录

    利用空闲时间把qmke的文档翻译出来,翻译水平有限,有些地方翻译的不好,请谅解, 如果您能指出来,我会很感激并在第一时候做出修改. 翻译qmake文档(一) qmake指南和概述 翻译qmake文档( ...

  8. Python汉英/英汉翻译(百度API/有道API)

    一.百度API实现 Step1:申请API Key 以前用过BAE,已经有了Api Key,没有的可以去申请 Step2:挺简单,直接看实现的代码吧 ```python #coding:utf-8 i ...

  9. macOS平台下Qt应用程序菜单翻译及调整

    一.翻译 在macOS平台上,系统会为应用程序菜单添加一些额外的菜单项.先来看一些典型的例子: 这个是Qt Creator的菜单,系统为应用程序菜单添加了一些桌面显示操作相关的菜单项: 这个是Qt D ...

  10. Android利用zxing生成二维码

    感谢大佬:https://blog.csdn.net/mountain_hua/article/details/80646089 **gayhub上的zxing可用于生成二维码,识别二维码 gayhu ...

随机推荐

  1. python flask获取微信用户信息流程

    需要了解的几个url 用户第一次访问时的url,包含以下几个参数 https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID& ...

  2. 90-Standard Deviation 标准离差指标.(2015.7.4)

    Standard Deviation 标准离差指标 ~计算: StdDev = SQRT (SUM (CLOSE - SMA (CLOSE, N), N)^2)/N 注解: SQRT - 正方体根: ...

  3. SpringMVC Controller的返回类型

    Controller的三种返回类型中 ModelAndView类型 带数据带跳转页面 String 跳转页面不带数据 void 通常是ajax格式请求时使用 1返回ModelAndView contr ...

  4. 指定PING的网卡

    struct ifreq ifr; // 绑定在eth0上 memset( &ifr, , sizeof( struct ifreq ) ); snprintf( ifr.ifr_name, ...

  5. Python安装配置

    Python下载 官网下载地址:https://www.python.org/downloads/windows/ 下载安装包: python-3.5.0-amd64(64位).exe python- ...

  6. 用二分法计算a的n次幂<算法分析>

    实验目的:1.复习java编程:2.掌握二分法的基本原理:3.掌握使用java程序进行二分法计算a的n次幂.实验步骤:1.由用户输入a及n(均为整数):2.利用二分法完成计算,并将中间结果打印出来. ...

  7. BNUOJ 5997 Fibonacci again and again

    Fibonacci again and again Time Limit: 1000ms Memory Limit: 32768KB This problem will be judged on HD ...

  8. Installing Zabbix 3.2 in Centos 6.8 Clean Install Dependencies Errors

    ZABBIX Forums > Zabbix Discussions and Feedback > Zabbix Troubleshooting and Problems > Ins ...

  9. HDU 4960 (水dp)

    Another OCD Patient Problem Description Xiaoji is an OCD (obsessive-compulsive disorder) patient. Th ...

  10. 2017-10-03-afternoon

    P100 zhx 竞赛时间:????年??月??日??:??-??:?? 题目名称 a b c 名称 a b c 输入 a.in b.in c.in 输出 a.out b.out c.out 每个测试 ...