[翻译]NUnit--Getting Started(二)
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(二)的更多相关文章
- Gradle 1.12用户指南翻译——第五十二章. Maven 插件
本文由CSDN博客貌似掉线翻译,其他章节的翻译请参见:http://blog.csdn.net/column/details/gradle-translation.html翻译项目请关注Github上 ...
- Gradle 1.12用户指南翻译——第三十二章. JDepend 插件
本文由CSDN博客万一博主翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Githu ...
- Gradle 1.12用户指南翻译——第四十二章. Announce插件
本文由CSDN博客万一博主翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Githu ...
- 深度学习论文翻译解析(十二):Fast R-CNN
论文标题:Fast R-CNN 论文作者:Ross Girshick 论文地址:https://www.cv-foundation.org/openaccess/content_iccv_2015/p ...
- 翻译qmake文档(二) Getting Started
翻译qmake文档 目录 原英文文档: http://qt-project.org/doc/qt-5/qmake-tutorial.html 本教程教讲授qmake基础知识.这个手册里 ...
- 中文翻译:pjsip教程(二)之ICE穿越打洞:Interactive Connectivity Establishment简介
1:pjsip教程(一)之PJNATH简介 2:pjsip教程(二)之ICE穿越打洞:Interactive Connectivity Establishment简介 3:pjsip教程(三)之ICE ...
- 翻译qmake文档 目录
利用空闲时间把qmke的文档翻译出来,翻译水平有限,有些地方翻译的不好,请谅解, 如果您能指出来,我会很感激并在第一时候做出修改. 翻译qmake文档(一) qmake指南和概述 翻译qmake文档( ...
- Python汉英/英汉翻译(百度API/有道API)
一.百度API实现 Step1:申请API Key 以前用过BAE,已经有了Api Key,没有的可以去申请 Step2:挺简单,直接看实现的代码吧 ```python #coding:utf-8 i ...
- macOS平台下Qt应用程序菜单翻译及调整
一.翻译 在macOS平台上,系统会为应用程序菜单添加一些额外的菜单项.先来看一些典型的例子: 这个是Qt Creator的菜单,系统为应用程序菜单添加了一些桌面显示操作相关的菜单项: 这个是Qt D ...
- Android利用zxing生成二维码
感谢大佬:https://blog.csdn.net/mountain_hua/article/details/80646089 **gayhub上的zxing可用于生成二维码,识别二维码 gayhu ...
随机推荐
- assert.ok()详解
assert.ok(value[, message]) 测试 value 是否为真值.它等同于 assert.equal(!!value, true, message). 如果 value 不是真值, ...
- 【HDU 2196】 Computer(树的直径)
[HDU 2196] Computer(树的直径) 题链http://acm.hdu.edu.cn/showproblem.php?pid=2196 这题可以用树形DP解决,自然也可以用最直观的方法解 ...
- CC3200 TI 笔记
I2C I2C总线是由Philips公司开发的一种简单.双向二线制同步串行总线.它只需要两根线即可在连接于总线上的器件之间传送信息. I2S I2S(Inter-IC Sound)总线, 又称 集成电 ...
- jQuery学习之------对标签属性的操作
jQuery学习之------标签的属性 <a href=””>链接</a>此处的href就是该a标签带有的属性 在js中对标签的属性的操作方法有 1.1getAttribut ...
- Django开发:(3.2)ORM:多表操作
表关系总结: 一对多:在多的表中建立关联字段 多对多:创建第三张表(关联表):id 和 两个关联字段 一对一:在两张表中的任意一张表中建立关联字段(关联字段一定要加 unique 约束) 子查询:一次 ...
- [NOIP2007] 提高组 洛谷P1097 统计数字
题目描述 某次科研调查时得到了n个自然数,每个数均不超过1500000000(1.5*10^9).已知不相同的数不超过10000个,现在需要统计这些自然数各自出现的次数,并按照自然数从小到大的顺序输出 ...
- 洛谷P1710地铁涨价
题目背景 本题开O2优化,请注意常数 题目描述 博艾市除了有海底高铁连接中国大陆.台湾与日本,市区里也有很成熟的轨道交通系统.我们可以认为博艾地铁系统是一个无向连通图.博艾有N个地铁站,同时有M小段地 ...
- CURL不可以读写文件
最近在学ES(elastic search),参考http://www.learnes.net/里面翻译的官方权威指南(后面发现官网已经推出了中文版文档了).里面有的例子把访问ES的命令做了简化如下: ...
- csu - 1538: Shopping (贪心)
http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1538 很奇妙的一个题,开始没有思路.问了别人才知道. 题目的意思可以理解成上图中,从0点开始向右走 ...
- Delphi第三方控件安装方式
由于组件提供的方式不同,所以安装的方法也是不一样的,下面就目前常见的各种形式的组 件的安装方法介绍一下. 1只有一个DCU文件的组件.DCU文件是编译好的单元文件,这 ...