以前的公司,开发工具都是 VS2003 2005 2008 , 对于单元测试, 都是去下载个 Nunit 来做单元测试的。
新公司的开发工具是 VS 2010, 我去看看 Nunit 的下载列表, 发现没有新版本了。
然后查询了一下,原来 VS2010 已经自带 单元测试的功能了, Nunit 没有更新的必要了……
今天做了一下单元测试的练习,使用起来,效果还是不错的。
找个被测试的类,右键 创建单元测试…… 就能把一个类的 测试代码的外壳都自动写好。
首先是被测试接口:
/// <summary>
/// 这个接口为 “被测试类” 的接口.
///
/// 用于 学习在 VS2010 中, 如何使用 测试项目 来测试的.
///
///
/// 操作的步骤如下:
/// 首先,被 测试项目,也就是这个 TestClassLib 项目
/// 然后,创建一个 测试项目,也就是 UnitTest 项目
///
/// 在定义好了 接口,也就是 IBase 之后。
/// 在接口 IBase.cs 这里 右键 选择 “创建单元测试...”
///
/// 在弹出窗口中,选择好要测试的 方法。
/// 然后确定 下面的 输出项目 为 UnitTest 项目后,按“确定”按钮.
/// 在 UnitTest 项目中,找到一个 IBaseTest.cs 的类
/// 在只有接口,没有写任何业务代码之前,可以开始写 测试代码.
///
///
/// </summary>
interface IBase
{
/// <summary>
/// 加法的接口.
/// </summary>
/// <param name="val1"></param>
/// <param name="val2"></param>
/// <returns></returns>
int Add(int val1, int val2);
/// <summary>
/// 减法的接口.
/// </summary>
/// <param name="val1"></param>
/// <param name="val2"></param>
/// <returns></returns>
int Sub(int val1, int val2);
/// <summary>
/// 连接字符串.
/// </summary>
/// <param name="val1"></param>
/// <param name="val2"></param>
/// <returns></returns>
String Concat(String val1, String val2);
/// <summary>
/// 连接字符串 并小写.
/// </summary>
/// <param name="val1"></param>
/// <param name="val2"></param>
/// <returns></returns>
String ConcatWithLowCase(String val1, String val2);
/// <summary>
/// 两个数字是否相等.
/// </summary>
/// <param name="val1"></param>
/// <param name="val2"></param>
/// <returns></returns>
bool IsEqual(int val1, int val2);
/// <summary>
/// 截取字符串.
/// </summary>
/// <param name="val"></param>
/// <param name="start"></param>
/// <param name="length"></param>
/// <returns></returns>
String SubString(String val, int start, int length);
}
然后是 被测试类
/// <summary>
/// 这个类为 “被测试类”
///
/// 用于 学习在 VS2010 中, 如何使用 测试项目 来测试的.
///
/// 对于代码覆盖率
/// 首先要双击 Local.testsettings
/// 在数据和诊断 的地方, 选择 代码覆盖率
/// 然后双击 代码覆盖率 在 “选择要检测的项目” 的地方, 选择 被测试的项目
/// 最后双击 在 Solution Items 下面的 VS2010Test.vsmdi
/// 进行测试。
///
/// 如果以运行 Test 项目的方式 来进行测试, 可能看不到 代码覆盖率
/// </summary>
public class BaseClass : IBase
{
/// <summary>
/// 加法.
/// </summary>
/// <param name="val1"></param>
/// <param name="val2"></param>
/// <returns></returns>
public int Add(int val1, int val2)
{
return val1 + val2;
}
/// <summary>
/// 减法.
/// </summary>
/// <param name="val1"></param>
/// <param name="val2"></param>
/// <returns></returns>
public int Sub(int val1, int val2)
{
return val1 - val2;
}
/// <summary>
/// 连接字符串.
/// </summary>
/// <param name="val1"></param>
/// <param name="val2"></param>
/// <returns></returns>
public String Concat(String val1, String val2)
{
return val1 + val2;
}
/// <summary>
/// 连接字符串 并小写.
/// </summary>
/// <param name="val1"></param>
/// <param name="val2"></param>
/// <returns></returns>
public String ConcatWithLowCase(String val1, String val2)
{
return val1.ToLower() + val2.ToLower();
}
/// <summary>
/// 两个数字是否相等.
/// </summary>
/// <param name="val1"></param>
/// <param name="val2"></param>
/// <returns></returns>
public bool IsEqual(int val1, int val2)
{
return val1 == val2;
}
/// <summary>
/// 截取字符串.
/// </summary>
/// <param name="val"></param>
/// <param name="start"></param>
/// <param name="length"></param>
/// <returns></returns>
public String SubString(String val, int start, int length)
{
if (start < 0)
{
return null;
}
if ((start + length) > val.Length)
{
return null;
}
if (length < 0)
{
return null;
}
return val.Substring(start, length);
}
}
/// <summary>
///这是 IBaseTest 的测试类,旨在
///包含所有 IBaseTest 单元测试
///</summary>
[TestClass()]
public class IBaseTest
{
private TestContext testContextInstance;
/// <summary>
///获取或设置测试上下文,上下文提供
///有关当前测试运行及其功能的信息。
///</summary>
public TestContext TestContext
{
get
{
return testContextInstance;
}
set
{
testContextInstance = value;
}
}
#region 附加测试特性
//
//编写测试时,还可使用以下特性:
//
//使用 ClassInitialize 在运行类中的第一个测试前先运行代码
//[ClassInitialize()]
//public static void MyClassInitialize(TestContext testContext)
//{
//}
//
//使用 ClassCleanup 在运行完类中的所有测试后再运行代码
//[ClassCleanup()]
//public static void MyClassCleanup()
//{
//}
//
//使用 TestInitialize 在运行每个测试前先运行代码
//[TestInitialize()]
//public void MyTestInitialize()
//{
//}
//
//使用 TestCleanup 在运行完每个测试后运行代码
//[TestCleanup()]
//public void MyTestCleanup()
//{
//}
//
#endregion
internal virtual IBase CreateIBase()
{
// 对于接口,首次创建的时候,是不知道是什么实现类的.
// 测试之前,需要修改具体的实现类.
// 也就是把 IBase target = null;
// 修改为被测试的实例类.
IBase target = new BaseClass();
return target;
}
/// <summary>
///Add 的测试
///</summary>
[TestMethod()]
public void AddTest()
{
IBase target = CreateIBase(); // TODO: 初始化为适当的值
int val1 = 1; // TODO: 初始化为适当的值
int val2 = 1; // TODO: 初始化为适当的值
int expected = 2; // TODO: 初始化为适当的值
int actual;
actual = target.Add(val1, val2);
// 验证指定的两个对象是否相等。如果两个对象不相等,则断言失败。
Assert.AreEqual(expected, actual);
// 向仍在处理的测试添加 Assert.Inconclusive 语句可指示该测试尚未准备好,不能运行。
//Assert.Inconclusive("验证此测试方法的正确性。");
}
/// <summary>
///Sub 的测试
///
/// 通过
/// 在“测试”菜单上,指向“窗口”,然后单击“测试视图”。
/// 在“测试视图”窗口中,右击要将数据源用于的单元测试,然后单击“属性”。
/// 在“属性”窗口中,单击“数据连接字符串”,然后单击省略号 (…)。
/// 按照“新建测试数据源向导”中的说明来创建数据连接。
///
/// 测试完毕后, 通过在测试结果那里, 右键-查看测试结果详细信息 (或者双击) 来看每一条数据的结果
///</summary>
[DataSource("System.Data.OleDb", "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=E:\\MySample\\VS2010Test\\TestData.accdb", "TestSub", DataAccessMethod.Sequential), TestMethod()]
public void SubTest()
{
IBase target = CreateIBase();
int val1 = Convert.ToInt32(TestContext.DataRow["val1"]);
int val2 = Convert.ToInt32(TestContext.DataRow["val2"]);
int expected = Convert.ToInt32(TestContext.DataRow["result"]);
int actual;
actual = target.Sub(val1, val2);
Assert.AreEqual(expected, actual);
//Assert.Inconclusive("验证此测试方法的正确性。");
}
/// <summary>
/// 对于一个 单元测试的 方法
/// 方法定义为 public void
/// 而且要定义好 TestMethod 标记.
/// </summary>
[TestMethod()]
public void ConcatTest()
{
// 初始化 被测试对象.
IBase target = CreateIBase();
// 定义好准备传入的参数.
String val1 = "Hello ";
String val2 = "World!";
// 定义好预期的返回值.
String expected = "Hello World!";
// 执行被测试方法.
String actual = target.Concat(val1, val2);
// 将预期的执行结果 与实际执行后的结果相比较.
Assert.AreEqual(expected, actual,
"调用Concat({0}, {1}) 预期结果为{2}, 实际执行结果为{3}", val1, val2, expected, actual);
// 上面的写法很繁琐.
// 简单的写.
Assert.AreEqual(expected, actual);
}
/// <summary>
/// 对于一个 单元测试的 方法
/// 方法定义为 public void
/// 而且要定义好 TestMethod 标记.
/// </summary>
[TestMethod()]
public void ConcatWithLowCaseTest()
{
// 初始化 被测试对象.
IBase target = CreateIBase();
// 定义好准备传入的参数.
String val1 = "Hello ";
String val2 = "World!";
// 定义好预期的返回值.
String expected = "Hello World!";
// 执行被测试方法.
String actual = target.ConcatWithLowCase(val1, val2);
// 预期 应该 与 实际 不相等.
Assert.AreNotEqual(expected, actual);
// 第三个参数 = true 指示所进行的比较不区分大小写。
// 在不区分大小写的情况下
// 预期 应该 与 实际 相等.
Assert.AreEqual(expected, actual, true);
}
/// <summary>
/// 对于一个 单元测试的 方法
/// 方法定义为 public void
/// 而且要定义好 TestMethod 标记.
/// </summary>
[TestMethod()]
public void IsEqualTest()
{
// 初始化 被测试对象.
IBase target = CreateIBase();
// 定义好准备传入的参数.
int val1 = 1024;
int val2 = 102;
// 执行被测试方法.
bool actual = target.IsEqual(val1, val2);
// 测试 返回值应该为 False.
Assert.IsFalse(actual);
// 执行被测试方法.
actual = target.IsEqual(val1, val1);
// 测试 返回值应该为 True.
Assert.IsTrue(actual);
}
/// <summary>
/// 对于一个 单元测试的 方法
/// 方法定义为 public void
/// 而且要定义好 TestMethod 标记.
/// </summary>
[TestMethod()]
public void SubStringTest()
{
// 初始化 被测试对象.
IBase target = CreateIBase();
// 定义好准备传入的参数.
String val = "123456789";
// 执行被测试方法.
String actual = target.SubString(val, -1, 6);
// 测试 返回值应该为 Null.
Assert.IsNull(actual);
// 执行被测试方法.
actual = target.SubString(val, 6, 6);
// 测试 返回值应该为 Null.
Assert.IsNull(actual);
// 执行被测试方法.
actual = target.SubString(val, 6, -6);
// 测试 返回值应该为 Null.
Assert.IsNull(actual);
// 执行被测试方法.
actual = target.SubString(val, 6, 2);
// 测试 返回值应该不为 Null.
Assert.IsNotNull(actual);
Assert.AreEqual("78", actual);
}
}