NUnit,第一眼让我想起来的名词是“单元测试”,是软件工程中的一个重要部分。测试的重要性的论述到处都是,种类当然也有很多。
测试真的只能是代码、软件的Bug Detector吗?当然原理上将就是找错,但在功用上讲,测试不仅可以作为“后期创作”的工具,如果将其运用于“前期准备”、“制作过程”中,也是一件利器。在TDD开发中,就显示了测试的重要性,以测试作为驱动力,结合设计,那是相当NB。
理论的东西看多了,没有实践就觉得虚。这段时间用C#开发,纯粹老土的人工测试,想想Java有JUnit(只是知道,也没用过,原来不大重视),C#用什么测呢?桌面应用又该怎么测呢?
于是乎,搜吧!这不,找到了NUnit,还是基于C#开源的,习之...
NUnit是一个为Net准备的自动化单元测试框架,它的作用就是帮助你方便的完成单元测试工作,同鼎鼎有名的JUnit一样,都是xUnit家族的成员。它的下载地址是:http://www.nunit.org。
详细的语法、功能,在网站上都有,我就不画蛇添足了,下面列出我写的例子,试验一下:
Account.cs
- using System;
- using System.Collections.Generic;
- using System.Text;
- namespace Bank
- {
- /// <summary>
- /// 银行帐户
- /// </summary>
- public class Account
- {
- /// <summary>
- /// 帐户余额
- /// </summary>
- private float _balance;
- /// <summary>
- /// 帐户最小余额
- /// </summary>
- private float _minBalance = 10.00f;
- /// <summary>
- /// 获取帐户余额
- /// </summary>
- public float Balance
- {
- get { return _balance; }
- }
- /// <summary>
- /// 获取帐户最小余额
- /// </summary>
- public float MinBalance
- {
- get { return _minBalance; }
- }
- /// <summary>
- /// 提款
- ///
- /// </summary>
- /// <param name="amount"></param>
- /// <returns></returns>
- public void WithDraw(float amount)
- {
- if (_balance - amount < MinBalance)
- throw new InsufficientMemoryException();
- _balance -= amount;
- }
- /// <summary>
- /// 存款
- /// </summary>
- /// <param name="amount"></param>
- public void Deposit(float amount)
- {
- _balance += amount;
- }
- /// <summary>
- /// 转帐
- /// 如果源帐户余额不足,触发异常
- /// </summary>
- /// <param name="destination"></param>
- /// <param name="amount"></param>
- /// <exception cref="Bank.InsuficientFundsException">转帐时帐户余额不足,抛出余额不足异常</exception>
- public void TransferFunds(Account destination, float amount)
- {
- if (_balance - amount < MinBalance)
- throw new InsufficientMemoryException();
- destination.Deposit(amount);
- WithDraw(amount);
- }
- }
- public class InsuficientFundsException : ApplicationException
- {
- }
- }
AccountTest.cs
- using System;
- using System.Collections.Generic;
- using System.Text;
- using NUnit.Framework;
- using NUnit.Framework.SyntaxHelpers;
- namespace Bank
- {
- [TestFixture]
- public class AccountTest
- {
- private Account source;
- private Account destination;
- /// <summary>
- /// <list type="bullet">
- /// <listheader>
- /// <term>Account</term>
- /// <description></description>
- /// <term>Init balance</term>
- /// <description></description>
- /// </listheader>
- /// <item>
- /// <term>source</term>
- /// <term>200.00</term>
- /// <term>destination</term>
- /// <term>150.00</term>
- /// </item>
- /// </list>
- /// </summary>
- [SetUp]
- public void Init()
- {
- source = new Account();
- source.Deposit(200.00f);
- destination = new Account();
- destination.Deposit(150.00f);
- }
- /// <summary>
- /// 成功存款测试
- /// </summary>
- [Test]
- [Category("Deposit"), Description("成功存款测试")]
- public void Deposit()
- {
- source.Deposit(100.00f);
- // Assert.AreEqual(300.00f, source.Balance);
- // Assert.That(300.00f, Is.EqualTo(source.Balance));
- Assert.That(300.00f == source.Balance);
- }
- /// <summary>
- /// 成功取款测试
- /// </summary>
- [Test]
- [Category("WidthDraw")]
- public void WidthDraw()
- {
- source.WithDraw(100.00f);
- // Assert.AreEqual(100.00f, source.Balance);//, "成功取款"
- Assert.That(100.00f, Is.EqualTo(source.Balance));
- }
- /// <summary>
- /// 取款余额不足测试
- /// </summary>
- [Test]
- [Category("WidthDraw")]
- [ExpectedException(typeof(InsufficientMemoryException))]
- public void WithDrawWithInsuficientFunds()
- {
- source.WithDraw(300.00f);
- }
- /// <summary>
- /// 成功转帐测试
- /// </summary>
- [Test]
- [Category("TransferFunds")]
- public void TransferFunds()
- {
- source.TransferFunds(destination, 150.00f);
- // Assert.AreEqual(50.00f, source.Balance);//, "成功转出{0},剩余{1}", 150.00f, source.Balance
- // Assert.AreEqual(300.00f, destination.Balance);
- Assert.That(50.00f, Is.EqualTo(source.Balance));
- Assert.That(300.00f, Is.EqualTo(destination.Balance));
- }
- /// <summary>
- /// 转帐余额不足测试
- /// </summary>
- [Test]
- [Category("TransferFunds")]
- [ExpectedException(typeof(InsufficientMemoryException))]
- public void TransferWithInsufficientFunds()
- {
- source.TransferFunds(destination, 300);
- }
- /// <summary>
- /// 转帐余额不足原子性测试
- /// </summary>
- [Test]
- [Category("TransferFunds")]
- [Ignore("Decide how to implements transaction management.")]
- public void TransferWithInsufficientFundsAtomicity()
- {
- try
- {
- source.TransferFunds(destination, 300);
- }
- catch
- {
- // 转帐异常,原帐户金额不变
- }
- //Assert.AreEqual(200, source.Balance);
- //Assert.AreEqual(150, destination.Balance);
- Assert.That(200.00f, Is.EqualTo(source.Balance));
- Assert.That(150.00f, Is.EqualTo(destination.Balance));
- }
- [Test]
- public void DirectlyFail()
- {
- Assert.Fail("Directly fail");
- }
- }
- }
为测试对象建立工程,一般是每个工程建立一个测试工程,测试工程中每个测试类对应一个需要测试的类,namespace也与其一致。这些是我想的习惯,这样比较容易理解,而且测试时的调用方法就可以作为其使用说明了。
将需要测试的工程,要求是类库,但我想估计exe工程也行,生成输出结果,添加引用到测试工程。
每个测试类由[TestFixture]标记,要求是public,有无参默认构造函数;
每个需要运行的测试方法由[Test]标记,要求返回void,无参;
测试就是进行一个断言判断,提供希望的到的结果、调用测试的业务逻辑,还可以提供测试结果描述信息,当运行时,结果只有成功与失败,当然还有一种情况是:抛出异常(可能是成功或失败,要看是否[ExpectedException(typeof())]了);
在众多的测试方法中,可能存在重复的初始化代码部分,当然可以Move to a method(重构的一种方法),然后调用。NUnit也提供了[SetUp]标记的方法,实现该功能,在每次运行测试方法时自动调用该方法,这样就不用你在测试方法中调用了,方便多了;
同[SetUp],还有[TearDown],功能差不多,只是在每次运行测试方法后执行。
有一个很好的标记[Ignore()],可以将未实现的测试方法标记起来,每次运行的时候不运行该方法,并且不计入运行结果中。这样可以将想到,但没有实现的测试保存下来,能提醒自己。
好了就简单介绍这些了。
上面讲的NUnit是一个独立的工具,如果在开发过程中进行测试,需要来回切换,比较麻烦。不怕,有神人把它做成了VS IDE的插件,不仅提供NUnit,而且还有MbUnit, NCover, NCoverExplorer, Reflecter, TypeMock。超强!!!它就是TestDriven.NET。官方网站http://www.testdriven.net/default.aspx
好了,现在C#也有测试工具了,不愁了,好好用吧~~~