单元测试作为直接对代码的测试是整个软件测试的基础。在面向对象编程中,单元测试一般针对类来实施。Visual Studio 2008既支持对类的公开接口进行测试,也能对私有属性和方法进行测试。在软件开发过程中,程序员一般都不愿意进行单元测试,认为单元测试要写很多“无用”的代码,“浪费”他们宝贵的开发时间。Visual Studio 2008提供了对单元测试的良好支持,只需要写少量的代码就可以完成单元测试,极大的提高了单元测试的效率。
关键字:单元测试,Visual Studio 2008
单元测试是在软件开发过程中要进行的最低级别的测试活动,在单元测试活动中,软件的独立单元将在与程序的其他部分相隔离的情况下进行测试。
在一种传统的结构化编程语言中,比如C,要进行测试的单元一般是函数或子过程。在象C++这样的面向对象的语言中, 要进行测试的基本单元是类。对Ada语言来说,开发人员可以选择是在独立的过程和函数,还是在Ada包的级别上进行单元测试。单元测试的原则同样被扩展到第四代语言(4GL)的开发中,在这里基本单元被典型地划分为一个菜单或显示界面。
单元测试不仅仅是作为无错编码的一种辅助手段在一次性的开发过程中使用,单元测试必须是可重复的,无论是在软件修改,或是移植到新的运行环境的过程中。因此,所有的测试都必须在整个软件系统的生命周期中进行维护。
Visual Studio 2008 单元测试功能介绍
一、测试代码与被测代码分离成独立的两个项目
单元测试中,测试的代码不能对被测试的代码施加影响。如果将测试代码写入被测试的代码中,测试完成后再删除的话,测试的正确性将得不到保证。因此,在Visual Studio 2008种提供了一种“Test Project”的项目,测试代码写在Test Project中,并且测试工程可以进行重复使用。
二、测试代码的自动生成
书写测试代码是一件很烦琐的事情,这些代码没有像程序代码一样具有“创造性”,因此该部分代码可以进行自动化生成。Visual Studio 2008就提供了一个自动生成测试代码的测试框架。利用Visual Studio 2008自动生成的代码,只需要很少的改动就可以完成整个测试程序。
三、测试管理
Visual Studio 2008提供了测试列表来进行测试工作的管理工作,我们需要一个反映目前测试状况的工具,那些测试通过了,那些没有通过,应该提供一个列表来为我们改进测试手段,进行更全面的测试提供指导。
利用Visual Studio 2008来进行单元测试
假设我们有一个类BankAccount,该类定义了一个银行的账户,私有属性_currentBalance是银行储户的账户金额,depositMoney是存款方法,对帐户增加一笔资金,makePayment是支付方法,对账户减少一笔资金。代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BankAccountDemo.Business
...{
class BankAccount
...{
private float _currentBalance;
public float CurrentBalance
...{
get ...{ return _currentBalance; }
set ...{ _currentBalance = value; }
}
public BankAccount(float initialBalance)
...{
this._currentBalance = initialBalance;
}
public void depositMoney(float depositAmount)
...{
this._currentBalance += depositAmount;
}
public void makePayment(float paymentAmount)
...{
this._currentBalance -= paymentAmount;
}
}
}
要对BankAccount类进行单元测试,只需要在BankAccount的定义处鼠标右键,在菜单中选择“Create Unit Tests”即可进入测试项目的创建工作。如下图所示:
在弹出的创建单元测试的对话框中,对需要创建测试的方法和属性进行选择,然后点击“OK”按钮,如图所示:
紧接着在出现的文本框中输入测试项目的名称“BankAccountDemo.Business.Tests”,点击确定后,测试项目被创建。在这里“BankAccountDemo.Business.”只是用于更好的对命名空间进行规划,完全可以直接使用“BankAccountDemoTest”来作为测试项目的名字。
生成的测试代码如下,为了紧凑的表现代码,将注释代码作了删除。
这个时候的代码并不能开始测试,而需要我们按照测试用例的要求将测试用例的数据加入到测试方法中,并进行结果的比较,修改后的depositMoneyTest方法如下:
[TestMethod()]
public void depositMoneyTest()
{
float initialBalance = 0F; // TODO: Initialize to an appropriate value
BankAccount target = new BankAccount(initialBalance); // TODO: Initialize to an appropriate value
float depositAmount = 100F; // TODO: Initialize to an appropriate value
target.depositMoney(depositAmount);
Assert.AreEqual(initialBalance + depositAmount, target.CurrentBalance, "Deposit Test: Deposit not applied correctly");
}
鼠标右键在depositMoneyTest方法内任意位置单击,在弹出的菜单中选择“Run Tests”,即可以对该方法进行测试。在“Test Results”窗口中显示测试的结果,如下图所示:
可以看出,Visual Studio 2008给我们提供了一个功能强大,操作简单的单元测试功能。利用该功能,程序员在编写代码后,可以马上对所编写的类进行单元测试,通过了程序员自行组织的单元测试后再将代码交给测试人员进行进一步测试。
总结:微软将单元测试功能从Visual Studio 2005 Team System开始集成到开发环境中,是经过了微软公司多年的实践经验证明的。如今,开发环境从以前的单一开发功能,将关注点分散到软件的整个生命周期过程中来,已经成为一个ALM平台。软件开发人员不仅需要做开发工作,而且需要对自己开发的代码进行单元测试,不能将所有的问题全部抛给测试人员。测试人员可以将更多的精力放在系统一级的测试工作上面。
using BankAccountDemo.Business;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace BankAccountDemo.Business.Tests
...{
[TestClass()]
public class BankAccountTest
...{
private TestContext testContextInstance;
public TestContext TestContext
...{
get
...{
return testContextInstance;
}
set
...{
testContextInstance = value;
}
}
Additional test attributes#region Additional test attributes
#endregion
[TestMethod()]
public void CurrentBalanceTest()
...{
float initialBalance = 0F; // TODO: Initialize to an appropriate value
BankAccount target = new BankAccount(initialBalance); // TODO: Initialize to an appropriate value
float expected = 0F; // TODO: Initialize to an appropriate value
float actual;
target.CurrentBalance = expected;
actual = target.CurrentBalance;
Assert.AreEqual(expected, actual);
Assert.Inconclusive("Verify the correctness of this test method.");
}
[TestMethod()]
public void makePaymentTest()
...{
float initialBalance = 0F; // TODO: Initialize to an appropriate value
BankAccount target = new BankAccount(initialBalance); // TODO: Initialize to an appropriate value
float paymentAmount = 0F; // TODO: Initialize to an appropriate value
target.makePayment(paymentAmount);
Assert.Inconclusive("A method that does not return a value cannot be verified.");
}
[TestMethod()]
public void depositMoneyTest()
...{
float initialBalance = 0F; // TODO: Initialize to an appropriate value
BankAccount target = new BankAccount(initialBalance); // TODO: Initialize to an appropriate value
float depositAmount = 0F; // TODO: Initialize to an appropriate value
target.depositMoney(depositAmount);
Assert.Inconclusive("A method that does not return a value cannot be verified.");
}
[TestMethod()]
public void BankAccountConstructorTest()
...{
float initialBalance = 0F; // TODO: Initialize to an appropriate value
BankAccount target = new BankAccount(initialBalance);
Assert.Inconclusive("TODO: Implement code to verify target");
}
}
}