[翻译]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 ...
随机推荐
- 配置Mysql审计
mysql-audit.json:Mysql审计日志 插件下载地址: https://bintray.com/mcafee/mysql-audit-plugin/release/1.1.4-725#f ...
- Vue实例方法之事件的实现
开始 这段时间一直在看vue的源码,源码非常多和杂,所以自己结合资料和理解理出了一个主线,然后根据主线去剥离其他的一些知识点,然后将各个知识点逐一学习.这里主要是分析的Vue事件处理的实现. 正文 一 ...
- Django之初
Django之初 Django的开始: #安装Django: pip3 install django #创建Django项目: django-admin startproject 项目名 #比如: d ...
- python书籍推荐:Python数据科学手册
所属网站分类: 资源下载 > python电子书 作者:today 链接:http://www.pythonheidong.com/blog/article/448/ 来源:python黑洞网 ...
- 慕课笔记利用css进行布局【两列布局】
<html> <head> <title>两列布局</title> <style type="text/css"> bo ...
- C++ stringstream的用法
Created at stringstream的用法 使用stringstream对象简化类型转换 C++标准库中的<sstream>提供了比ANSI C的<stdio.h&g ...
- 593. Valid Square
Problem statement: Given the coordinates of four points in 2D space, return whether the four points ...
- android开发里跳过的坑——button不响应点击事件
昨天遇到一个头疼的问题,在手机上按钮事件都很正常,但是在平板上(横屏显示的状态),button点击事件不响应,代码简化如下: public class Test extends Activity im ...
- 【ZJOI2017 Round1练习&UVA1057】D6T1 Routing(DP,SPFA)
题意:给你一个有向图, 并指定起点和终点. 问要从起点走向终点, 再从终点走向起点, 最少需要走过多少不同的节点. 对于 100%的数据, 有 N<=100, M<=min(1000,N* ...
- Codeforces704C. Black Widow
n<=1e5个值v,分别由<=1e5的m个变量中的1<=ki<=2个布尔变量xj(或某个变量取反)或起来组成,而所有的v异或起来为1,一个x不会在输入数据中出现超过2次,包括他 ...