实践单元测试(3)-Using NUnit

NUnit是.net平台上使用得最为广泛的测试框架之一,本文将通过示例来描述NUnit的使用方法,并提供若干编写单元测试的建议和技巧,供单元测试的初学者参考。

继续下文之前,先来看看一个非常简单的测试用例(TestCase):
1 [Test]
2 public void AdditionTest()
3 {
4     int expectedResult = 2;
5 
6     Assert.AreEqual(exptectedResult, 1 + 1);
7 }
你肯定会说这个TestCase也太白痴了吧!这也是许多NUnit文档被人诟病的一点,但是我的理解并不是这样,xUnit本来就是编写UT的简易框 架,keep it simple and stupid,任何通过复杂的TestCase来介绍NUnit的用法都是一种误导,UT复杂之处在于如何在实际项目中应用和实施,而不是徘徊于该如何使 用NUnit。

主要内容:
1、NUnit的基本用法
2、测试用例的组织
3、NUnit的断言(Assert)
4、常用单元测试工具介绍

一、NUnit的基本用法
和 其他xNUnit框架不同的是,NUnit框架使用Attribute(如前面代码中的[Test])来描述测试用例的,也就是说我们只要掌握了 Attribute的用法,也就基本学会如何使用NUnit了。VSTS所集成的单元测试也支持类似NUnit的Attributes,下表对比了 NUnit和VSTS的标记:

usage

NUnit attributes

VSTS attributes

标识测试类

TestFixture

TestClass

标识测试用例(TestCase

Test

TestMethod

标识测试类初始化函数

TestFixtureSetup

ClassInitialize

标识测试类资源释放函数

TestFixtureTearDown

ClassCleanup

标识测试用例初始化函数

Setup

TestInitialize

标识测试用例资源释放函数

TearDown

TestCleanUp

标识测试用例说明

N/A

Description

标识忽略该测试用例

Ignore

Ignore

标识该用例所期望抛出的异常

ExpectedException

ExpectedException

标识测试用例是否需要显式执行

Explicit

?

标识测试用例的分类

Category

?


现在,让我们找一个场景,通过示例来了解上述NUnit标记的用法。来看看一个存储在数据库中的数字类:

这是我们常见的DAL+Entity的设计,DigitDataProvider和Digit类的实现代码如下:

1)Digit.cs类:
 1 using System;
 2 using System.Data;
 3 
 4 namespace Product
 5 {
 6     /// <summary>
 7     /// Digit 的摘要说明
 8     /// </summary>
 9     /// 创 建 人: Aero
10     /// 创建日期: 2006-3-22
11     /// 修 改 人: 
12     /// 修改日期:
13     /// 修改内容:
14     /// 版    本:
15     public class Digit
16     {
17         private Guid _digitId;
18         public Guid DigitID
19         {
20             get { return this._digitId; }
21             set { this._digitId = value; }
22         }
23 
24         private int _value = 0;
25         public int Value
26         {
27             get { return this._value; }
28             set { this._value = value; }
29         }
30 
31         #region 构造函数
32         /// <summary>
33         /// 默认无参构造函数
34         /// </summary>
35         /// 创 建 人: Aero
36         /// 创建日期: 2006-3-22
37         /// 修 改 人: 
38         /// 修改日期:
39         /// 修改内容:
40         public Digit()
41         {
42             //
43             // TODO: 在此处添加构造函数逻辑
44             //
45         }
46 
47         /// <summary>
48         /// construct the digit object from a datarow
49         /// </summary>
50         /// <param name="row"></param>
51         public Digit(DataRow row)
52         {
53             if (row == null)
54             {
55                 throw new ArgumentNullException();
56             }
57 
58             if (row["DigitID"!= DBNull.Value)
59             {
60                 this._digitId = new Guid(row["DigitID"].ToString());
61             }
62 
63             if (row["Value"!= DBNull.Value)
64             {
65                 this._value = Convert.ToInt32(row["Value"]);
66             }
67         }
68         
69         #endregion
70     }
71 }
72 

2)DigitDataProvider类:
  1 using System;
  2 using System.Data;
  3 using System.Data.SqlClient;
  4 using System.Collections;
  5 
  6 namespace Product
  7 {
  8     /// <summary>
  9     /// DigitDataProvider 的摘要说明
 10     /// </summary>
 11     /// 创 建 人: Aero
 12     /// 创建日期: 2006-3-22
 13     /// 修 改 人: 
 14     /// 修改日期:
 15     /// 修改内容:
 16     /// 版    本:
 17     public class DigitDataProvider
 18     {
 19         /// <summary>
 20         /// 定义数据库连接
 21         /// </summary>
 22         private SqlConnection _dbConn;
 23         public SqlConnection Connection
 24         {
 25             get { return this._dbConn; }
 26             set { this._dbConn = value; }
 27         }
 28         
 29         #region 构造函数
 30         /// <summary>
 31         /// 默认无参构造函数
 32         /// </summary>
 33         /// 创 建 人: Aero
 34         /// 创建日期: 2006-3-22
 35         /// 修 改 人: 
 36         /// 修改日期:
 37         /// 修改内容:
 38         public DigitDataProvider()
 39         {
 40             //
 41             // TODO: 在此处添加构造函数逻辑
 42             //
 43         }
 44 
 45         public DigitDataProvider(SqlConnection conn)
 46         {
 47             this._dbConn = conn;
 48         }
 49         
 50         #endregion
 51         
 52         #region 成员函数定义
 53 
 54         /// <summary>
 55         /// retrieve all Digits in the database
 56         /// </summary>
 57         /// <returns></returns>
 58         public ArrayList GetAllDigits()
 59         {
 60             // retrieve all digit record in database
 61             SqlCommand command = this._dbConn.CreateCommand();
 62             command.CommandText = "SELECT * FROM digits";
 63             SqlDataAdapter adapter = new SqlDataAdapter(command);
 64             DataSet results = new DataSet();
 65             adapter.Fill(results);
 66 
 67             // convert rows to digits collection
 68             ArrayList digits = null;
 69 
 70             if (results != null && results.Tables.Count > 0)
 71             {
 72                 DataTable table = results.Tables[0];
 73                 digits = new ArrayList(table.Rows.Count);
 74 
 75                 foreach (DataRow row in table.Rows)
 76                 {
 77                     digits.Add(new Digit(row));
 78                 }
 79             }
 80 
 81             return digits;
 82         }
 83 
 84         /// <summary>
 85         /// remove all digits from the database
 86         /// </summary>
 87         /// <returns></returns>
 88         public int RemoveAllDigits()
 89         {
 90             // retrieve all digit record in database
 91             SqlCommand command = this._dbConn.CreateCommand();
 92             command.CommandText = "DELETE FROM digits";
 93 
 94             return command.ExecuteNonQuery();
 95         }
 96 
 97         /// <summary>
 98         /// retrieve and return the entity of given value
 99         /// </summary>
100         /// <exception cref="System.NullReferenceException">entity not exist in the database</exception>
101         /// <param name="value"></param>
102         /// <returns></returns>
103         public Digit GetDigit(int value)
104         {
105             // retrieve entity of given value
106             SqlCommand command = this._dbConn.CreateCommand();
107             command.CommandText = "SELECT * FROM digits WHERE Value='" + value + "'";
108             SqlDataAdapter adapter = new SqlDataAdapter(command);
109             DataSet results = new DataSet();
110             adapter.Fill(results);
111 
112             // convert rows to digits collection
113             Digit digit = null;
114 
115             if (results != null && results.Tables.Count > 0
116                 && results.Tables[0].Rows.Count > 0)
117             {
118                 digit = new Digit(results.Tables[0].Rows[0]);
119             }
120             else
121             {
122                 throw new NullReferenceException("not exists entity of given value");
123             }
124 
125             return digit;
126         }
127 
128         /// <summary>
129         /// remove prime digits from database
130         /// </summary>
131         /// <returns></returns>
132         public int RemovePrimeDigits()
133         {
134             throw new NotImplementedException();
135         }
136 
137         #endregion
138     }
139 }
140 

3)新建测试数据库:
CREATE TABLE [dbo].[digits] (
    
[DigitID] [uniqueidentifier] NOT NULL ,
    
[Value] [int] NOT NULL 
ON [PRIMARY]
GO


下面,我们开始尝试为DigitDataProvider类编写UT,新建DigitDataProviderTest.cs类。
1、添加nunit.framework引用

并在DigitDataProviderTest.cs中添加:
1 using NUnit.Framework;

2、编写测试用例
1)标识测试类:NUnit要求每个测试类都必须添加TestFixture的Attribute,并且携带一个public无参构造函数。
1 [TestFixture]
2 public class DigitProviderTest
3 {
4     public DigitProviderTest()
5     {
6     }
7 }

2)编写DigitDataProvider.GetAllDigits()的测试函数
 1 /// <summary>
 2 /// regular test of DigitDataProvider.GetAllDigits()
 3 /// </summary>
 4 [Test]
 5 public void TestGetAllDigits()
 6 {
 7     // initialize connection to the database
 8     // note: change connection string to ur env
 9     IDbConnection conn = new SqlConnection(
10         "Data source=localhost;user id=sa;password=sa;database=utdemo");
11     conn.Open();
12 
13     // preparing test data
14     IDbCommand command = conn.CreateCommand();
15     string commadTextFormat = "INSERT INTO digits(DigitID, Value) VALUES('{0}', '{1}')";
16 
17     for (int i = 1; i <= 100; i++)
18     {
19         command.CommandText = string.Format(
20             commadTextFormat, Guid.NewGuid().ToString(), i.ToString());
21         command.ExecuteNonQuery();
22     }
23 
24     // test DigitDataProvider.GetAllDigits()
25     int expectedCount = 100;
26     DigitDataProvider provider = new DigitDataProvider(conn as SqlConnection);
27     IList results = provider.GetAllDigits();
28 
29     // that works?
30     Assert.IsNotNull(results);
31     Assert.AreEqual(expectedCount, results.Count);
32 
33     // delete test data
34     command = conn.CreateCommand();
35     command.CommandText = "DELETE FROM digits";
36     command.ExecuteNonQuery();
37 
38     // close connection to the database
39     conn.Close();
40 }

什么?很丑?很麻烦?这个问题稍后再讨论,先来看看一个完整的测试用例该如何定义:
 1 [Test]
 2 public void TestCase()
 3 {
 4     // 1) initialize test environement, like database connection
 5     
 6 
 7     // 2) prepare test data, if neccessary
 8     
 9 
10     // 3) test the production code by using assertion or Mocks.
11     
12 
13     // 4) clear test data
14     
15 
16     // 5) reset the environment
17     
18 }
NUnit要求每一个测试函数都可以独立运行(往往有人会误解NUnit并按照Consoler中的排序来执行),这就要求我们在调用目标函数之前先要初 始化目标函数执行所需要的环境,如打开数据库连接、添加测试数据等。为了不影响其他的测试函数,在调用完目标函数后,该测试函数还要负责还原初始环境,如 删除测试数据和关闭数据库连接等。对于同一测试类里的测试函数来说,这些操作往往是相同的,让我们对上面的代码进行一次Refactoring, Extract Method:
 1 /// <summary>
 2 /// connection to database
 3 /// </summary>
 4 private static IDbConnection _conn;
 5 
 6 /// <summary>
 7 /// 初始化测试类所需资源
 8 /// </summary>
 9 [TestFixtureSetUp]
10 public void ClassInitialize()
11 {
12     // note: change connection string to ur env
13     DigitProviderTest._conn = new SqlConnection(
14         "Data source=localhost;user id=sa;password=sa;database=utdemo");
15     DigitProviderTest._conn.Open();
16 }
17 
18 /// <summary>
19 /// 释放测试类所占用资源
20 /// </summary>
21 [TestFixtureTearDown]
22 public void ClassCleanUp()
23 {
24     DigitProviderTest._conn.Close();
25 }
26 
27 /// <summary>
28 /// 初始化测试函数所需资源
29 /// </summary>
30 [SetUp]
31 public void TestInitialize()
32 {
33     // add some test data
34     IDbCommand command = DigitProviderTest._conn.CreateCommand();
35     string commadTextFormat = "INSERT INTO digits(DigitID, Value) VALUES('{0}', '{1}')";
36 
37     for (int i = 1; i <= 100; i++)
38     {
39         command.CommandText = string.Format(
40             commadTextFormat, Guid.NewGuid().ToString(), i.ToString());
41         command.ExecuteNonQuery();
42     }
43 }
44 
45 /// <summary>
46 /// 释放测试函数所需资源
47 /// </summary>
48 [TearDown]
49 public void TestCleanUp()
50 {
51     // delete all test data
52     IDbCommand command = DigitProviderTest._conn.CreateCommand();
53     command.CommandText = "DELETE FROM digits";
54 
55     command.ExecuteNonQuery();
56 }
57 
58 /// <summary>
59 /// regular test of DigitDataProvider.GetAllDigits()
60 /// </summary>
61 [Test]
62 public void TestGetAllDigits()
63 {
64     int expectedCount = 100;
65     DigitDataProvider provider = 
66         new DigitDataProvider(DigitProviderTest._conn as SqlConnection);
67 
68     IList results = provider.GetAllDigits();
69     // that works?
70     Assert.IsNotNull(results);
71     Assert.AreEqual(expectedCount, results.Count);
72 }
NUnit提供了以下Attribute来支持测试函数的初始化:
TestFixtureSetup:在当前测试类中的所有测试函数运行前调用;
TestFixtureTearDown:在当前测试类的所有测试函数运行完毕后调用;
Setup:在当前测试类的每一个测试函数运行前调用;
TearDown:在当前测试类的每一个测试函数运行后调用。

3)编写DigitDataProvider.RemovePrimeDigits()的测试函数
唉,又忘了质数判断的算法,这个函数先不实现(throw new NotImplementedException()),对应的测试函数先忽略。
 1 /// <summary>
 2 /// regular test of DigitDataProvider.RemovePrimeDigits
 3 /// </summary>
 4 [Test, Ignore("Not Implemented")]
 5 public void TestRemovePrimeDigits()
 6 {
 7     DigitDataProvider provider = 
 8         new DigitDataProvider(DigitProviderTest._conn as SqlConnection);
 9 
10     provider.RemovePrimeDigits();
11 }
Ignore的用法:
Ignore(string reason)


4)编写DigitDataProvider.GetDigit()的测试函数
当查找一个不存在的Digit实体时,GetDigit()会不会像我们预期一样抛出NullReferenceExceptioin呢?
 1 /// <summary>
 2 /// Exception test of DigitDataProvider.GetDigit()
 3 /// </summary>
 4 [Test, ExpectedException(typeof(NullReferenceException))]
 5 public void TestGetDigit()
 6 {
 7     int expectedValue = 999;
 8     DigitDataProvider provider = 
 9         new DigitDataProvider(DigitProviderTest._conn as SqlConnection);
10 
11     Digit digit = provider.GetDigit(expectedValue);
12 }
ExpectedException的用法
ExpectedException(Type t)
ExpectedException(Type t, 
string expectedMessage)


在NUnitConsoler里执行一把,欣赏一下黄绿灯吧。本文相关代码可从UTDemo_Product.rar下载。

二、测试函数的组织
现在有一个性能测试的Testcase,执行一次要花上一个小时,我们并不需要(也无法忍受)每次自动化测试时都去执行这样的Testcase,使用NUnit的Explicit标记可以让这个TestCase只有在显示调用下才会执行:
1 [Test, Explicit]
2 public void OneHourTest()
3 {
4     //
5 }



不幸的是,这样耗时的TestCase在整个测试工程中可能有数十个,或许更多,我们能不能把这些TestCase都组织起来,要么一起运行,要么不运行呢?NUnit提供的Category标记可实现此功能:
 1 [Test, Explicit, Category("LongTest")]
 2 public void OneHourTest()
 3 {
 4     ...
 5 }
 6 
 7 [Test, Explicit, Category("LongTest")]
 8 public void TwoHoursTest()
 9 {
10     ...
11 }
这样,只有当显示选中LongTest分类时,这些TestCase才会执行


三、NUnit的断言
NUnit提供了一个断言类NUnit.Framework.Assert,可用来进行简单的state base test(见idior的Enterprise Test Driven Develop),可别对这个断言类期望太高,在实际使用中,我们往往需要自己编写一些高级断言。
常用的NUnit断言有:

method

usage

example

Assert.AreEqual(object expected, object actual[, string message])

验证两个对象是否相等

Assert.AreEqual(2, 1+1)

Assert.AreSame(object expected, object actual[, string message])

验证两个引用是否指向同意对象

object expected = new object();

object actual = expected;

Assert.AreSame(expected, actual)

Assert.IsFalse(bool)

验证bool值是否为false

Assert.IsFalse(false)

Assert.IsTrue(bool)

验证bool值是否为true

Assert.IsTrue(true)

Assert.IsNotNull(object)

验证对象是否不为null

Assert.IsNotNull(new object())

Assert.IsNull(object)

验证对象是否为null

Assert.IsNull(null);


这 里要特殊指出的Assert.AreEqual只能处理基本数据类型和实现了Object.Equals接口的对象的比较,对于我们自定义对象的比较,通 常需要自己编写高级断言,这个问题郁闷了我好一会,下面给出一个用于level=1的情况下的对象比较的高级断言的实现:
 1 public class AdvanceAssert
 2 {
 3     /// <summary>
 4     /// 验证两个对象的属性值是否相等
 5     /// </summary>
 6     /// <remarks>
 7     /// 目前只支持的属性深度为1层
 8     /// </remarks>
 9     public static void AreObjectsEqual(object expected, object actual)
10     {
11         // 若为相同引用,则通过验证
12         if (expected == actual)
13         {
14             return;
15         }
16 
17         // 判断类型是否相同
18         Assert.AreEqual(expected.GetType(), actual.GetType());
19 
20         // 测试属性是否相等
21         Type t = expected.GetType();
22         PropertyInfo[] properties = t.GetProperties(BindingFlags.Instance | BindingFlags.Public);
23 
24         foreach (PropertyInfo property in properties)
25         {
26             object obj1 = t.InvokeMember(property.Name, 
27                 BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty, 
28                 null, expected, null);
29             object obj2 = t.InvokeMember(property.Name, 
30                 BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty, 
31                 null, actual, null);
32 
33             // 判断属性是否相等
34             AdvanceAssert.AreEqual(obj1, obj2, "assertion failed on " + property.Name);
35         }
36     }
37 
38     /// <summary>
39     /// 验证对象是否相等
40     /// </summary>
41     private static void AreEqual(object expected, object actual, string message)
42     {
43         Type t = expected.GetType();
44 
45         if (t.Equals(typeof(System.DateTime)))
46         {
47             Assert.AreEqual(expected.ToString(), actual.ToString(), message);
48         }
49         else
50         {
51             // 默认使用NUnit的断言
52             Assert.AreEqual(expected, actual, message);
53         }
54     }
55 }
56 

四、常用单元测试工具介绍:
1、NUnit:目前最高版本为2.2.7(也是本文所使用的NUnit的版本)
下载地址:http://www.nunit.org

2、TestDriven.Net:一款把NUnit和VS IDE集成的插件
下载地址:http://www.testdriven.net/

3、NUnit2Report:和nant结合生成单元测试报告
下载地址:http://nunit2report.sourceforge.net

4、Rhino Mocks 2:个人认为时.net框架下最好的mocks库,而且支持.net 2.0, rocks~!
下载地址:http://www.ayende.com/projects/rhino-mocks.aspx


想不到一口气写了这么多,前段时间在公司的项目中进行了一次单元测试的尝试,感触很深,看了idior的文章后更加觉得单元测试日后会成为项目的必需部分。在后续的文章中,我将讨论mocks,自定义测试框架和自动化测试工具,希望能和园子里的uter多多讨论。

好向往TDD~~

posted on 2006-03-23 00:56 Aero 阅读(5633) 评论(34)  编辑 收藏 网摘 所属分类: DotNet敏捷开发单元测试

评论

#1楼 2006-03-23 08:53 i服了yuo[未注册用户]

真服了楼主,居然有时间写这么长的垃圾文章,居然还放在首页。   回复  引用    

#2楼 2006-03-23 09:01 玻璃*杯[未注册用户]

楼主不错的文章,希望马上能看MOCK的文章.
只要能share自己的东西就是好文章.
  回复  引用    

#3楼 2006-03-23 09:08 Riancy[未注册用户]

我觉得很好啊,对于想学习"通NUNIT实现单元测试"的朋友来说是有帮助的!希望楼主继续努力   回复  引用    

#4楼 2006-03-23 09:14 baoli[未注册用户]

通俗易懂,我喜欢,支持楼主!   回复  引用    

#5楼 2006-03-23 09:15 jzhye[未注册用户]

很好   回复  引用    

#6楼 2006-03-23 09:26 lyb      

非常不错,很好啊!多谢!   回复  引用  查看    

#7楼[楼主] 2006-03-23 09:57 Aero      

@i服了yuo

我接收你的批评,但我同样bs你。只要我认为值得分享,花几个小时也值得。
  回复  引用  查看    

#8楼 2006-03-23 09:58 三人行必有我师[未注册用户]

写的很好,很好的文章!!!
问一下你代码里下面这些是怎么设置的,阿阿~
33 /// 创 建 人: Aero
34 /// 创建日期: 2006-3-22
35 /// 修 改 人:
36 /// 修改日期:
37 /// 修改内容:
  回复  引用    

#9楼 2006-03-23 10:47 idior      

A simple way to roll back DB pollution in Test

http://idior.cnblogs.com/archive/2005/07/29/202860.html">http://idior.cnblogs.com/archive/2005/07/29/202860.html
  回复  引用  查看    

#10楼 2006-03-23 11:45 wxg[未注册用户]

支持Aero!强烈bs那个大嘴巴!   回复  引用    

#11楼 2006-03-23 12:42 Terrylee      

@i服了yuo
看了你的回复,我忍不住想问几句
这篇文章到底垃圾在哪儿了?
再问一句,你又写过哪几篇文章,可否拿出来供大家一看?
还想说一句,看你这样的语言,甭管你的技术水平有多高,至少人品……

每个人都有自己认为值得共享的东西,不要总拿自己的意愿强加在别人的头上,再说这篇文章就算对你没用,对初学者也是很好的一篇文章,大家从中学到了东西,何乐而不为呢?
  回复  引用  查看    

#12楼 2006-03-23 14:02 wayfarer      

写得很不错,而且很详细。算是一个很好的Guide文档了。支持!

btw:博客上这种大嘴巴太多了,根本不用理会。
  回复  引用  查看    

#13楼[楼主] 2006-03-23 14:12 Aero      

@三人行必有我师

你看看%VS2003安装目录%\VC#\VC#Wizards\CSharpAddClassWiz\Templates\2052下的NewCSharpFile.cs文件,这是新增Class的模板,修改即可。
  回复  引用  查看    

#14楼[楼主] 2006-03-23 14:20 Aero      

@idior

哇,用COM+1.5的方式实现数据的undo太酷了,谢谢idior大哥的推荐
  回复  引用  查看    

#15楼[楼主] 2006-03-23 14:21 Aero      

@Terrylee and wayfarer

谢谢大家的支持,我们不必理会这样的大嘴巴
  回复  引用  查看    

#16楼 2006-03-23 18:27 idior      

@Aero
不客气,这篇文章写的不错。欢迎加入D&P Team,期待更多好的文章。
  回复  引用  查看    

#17楼 2006-03-23 21:02 laifangsong      

好文章,期待Aero 多写几篇   回复  引用  查看    

#18楼 2006-03-28 17:00 风一样的狂徒11      

谢谢作者,我刚刚开始使用NUnit,文章使我收获不少   回复  引用  查看    

#19楼 2006-04-03 22:39 MS的明天      

写的很清楚,期待后续文章   回复  引用  查看    

#20楼 2006-04-18 15:30 xiu dou le[未注册用户]

虽然我是个新人,我也觉得不错,还收藏了,能否留个联系方式啊,有空多指教啊!   回复  引用    

#21楼 2006-04-26 09:46 stu_acer[未注册用户]

楼主写的不错,这篇文章给我这个初学者很好的启发,赞一个!   回复  引用    

#22楼 2006-06-02 14:20 showshore[未注册用户]

我知道怎样用NUNIT来写单元测试,但就是没能明白怎么运用到实际项目中。看了楼主的这篇文章,感触很深,可以自己多试试了。谢谢   回复  引用    

#23楼 2006-06-08 20:15 Anderslly      

感谢楼主,赞!   回复  引用  查看    

#24楼 2006-06-29 16:04 iew[未注册用户]

very good !!!!   回复  引用    

#25楼 2006-06-30 13:53 aaaaaa[未注册用户]

<a href="http://www.toutsie.com//">参考</a>
[URL=http://www.rags.com/]简介[/URL]
  回复  引用    

#26楼 2006-12-06 14:52 Anthan      

支持作者,我也是刚开始使用的。
也许很多所谓的高手不屑使用Nunit做测试,亦或许对自己的代码太过自信。
但是永远也不会有绝对正确的代码。
所以还是要学习好的测试方法。
  回复  引用  查看    

#27楼 2007-02-07 17:41 swsw1979[未注册用户]

我机器上同时装有v1.1.4322和v2.0.50727,我装的nunit 只可以打开v1.1.4322编译的dll,打开v2.0.50727的版本总是报错。我试着
nunit-gui.exe /framework='v2.0.50727'或nunit-gui.exe /framework:v2.0.50727都报错。You may be attempting to load an assembly bulit with a later version of the CLR than the version under which is crrently running,1.1.4322.For further information,use the Exception Details menu item.该怎么解决呢?
  回复  引用    

#28楼 2007-04-04 14:03 kevin[未注册用户]

谢谢楼主,我是刚刚接触单元测试的,希望以后多多邦忙.   回复  引用    

#29楼 2007-04-16 13:37 数据恢复[未注册用户]

写的非常好好 我收藏了
事事顺利!----[url=http://www.sitit.com]上海数据恢复[/url]
笑口常开!----[url=http://www.cndatasos.com]数据恢复[/url]
  回复  引用    

#30楼 2007-09-17 15:31 zxg[未注册用户]

楼主,可以把完整的例子发给我吗?zxgnoke@gmail.com
先谢啦!
  回复  引用    

#31楼 2008-01-25 11:07 竹纤维[未注册用户]

<a href="http://www.tenbro.com">竹纤维T恤</a>
<a href="http://www.tenbro.com">竹纤维毛巾</a>
<a href="http://www.tenbro.com">天竹纤维</a>
<a href="http://www.tenbro.com">天竹纱线</a>
<a href="http://www.tenbro.com">竹纤维</a>
  回复  引用    

#32楼 2009-05-05 08:44 kobewang[未注册用户]

对新手来说 你的帮助是无穷的 继续努力   回复  引用    




发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 356326




相关文章:

相关链接:

导航

公告

从不了解到了解,从了解到熟悉,从熟悉到精通,这纪录了一个人思考和解决问题的成长过程。

转载请遵守 创作共用约定

关于语音参数分析平台: 毕设也博客

msn: wk_kevin at hotmail.com
email: chwkai at gmail.com
Free Website Counter
<2006年3月>
2627281234
567891011
12131415161718
19202122232425
2627282930311
2345678

统计

与我联系

搜索

 

常用链接

留言簿

我参加的小组

我参与的团队

随笔分类(66)

随笔档案(59)

文章分类(18)

相册

about Aero

friendz

project bloggerz

project sitez

techz

积分与排名

最新评论

阅读排行榜

评论排行榜