[翻译]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 ...
随机推荐
- [Python3网络爬虫开发实战] 7.3-Splash负载均衡配置
用Splash做页面抓取时,如果爬取的量非常大,任务非常多,用一个Splash服务来处理的话,未免压力太大了,此时可以考虑搭建一个负载均衡器来把压力分散到各个服务器上.这相当于多台机器多个服务共同参与 ...
- JSON对象与JSON字符串之间的转换
JSON引用包:import net.sf.json 1.JSON字符串转JSON对象(例子中的AgencyExpand是java对象) JSONObject json = JSONObject.f ...
- 创建Tensor
目录 创建Tensor numpy, list numpy list zeros, ones, fill zeros ones fill random 打乱idx后,a和b的索引不变 constant ...
- MySQL-----多对多
多对多: 示例1: 用户表和相亲记录表 用户表 用户id 用户名 性别 1 George 男 2 Elizabeth 女 3 Bruce 男 4 Catherine ...
- Ubuntu系统搭建django+nginx+uwsgi
1. 在开发机上的准备工作 2. 在服务器上的准备工作 3.安装uwsgi 4.编写uwsgi配置文件,使用配置文件启动uwsgi 5. 安装nginx 6. 收集静态文件 7. 编写nginx配置文 ...
- 九度oj 题目1438:最小公倍数
题目1438:最小公倍数 时间限制:1 秒 内存限制:128 兆 特殊判题:否 提交:2451 解决:2057 题目描述: 给定两个正整数,计算这两个数的最小公倍数. 输入: 输入包含多组测试数据,每 ...
- 1004. 成绩排名 (20) (快速排序qsort函数的使用问题)
读入n名学生的姓名.学号.成绩,分别输出成绩最高和成绩最低学生的姓名和学号. 输入格式:每个测试输入包含1个测试用例,格式为 第1行:正整数n 第2行:第1个学生的姓名 学号 成绩 第3行:第2个学生 ...
- vue2.0一个书城实例
gitHub克隆地址 git clone https://github.com/Webxiaoyaun/vue-book.git 点击去Github下载 ## 一个书城 ## 有增加,修改,缓存,懒加 ...
- 【小记事】电脑命令行开WiFi
1.设置WiFi名称和密码 在命令行输入: netsh wlan set hostednetwork mode=allow WiFi名称 key=密码 2.开启WiFi 在命令行输入: netsh w ...
- request详究
本文主要是对在学习过程中遇到的request用法进行归纳总结,彻底的搞明白request在jsp中的作用. 百度百科的介绍如下: Request对象的作用是与客户端交互,收集客户端的Form.Cook ...