﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>IT博客-咚咚咚-文章分类-Nunit &amp; .Net</title><link>http://www.cnitblog.com/tab1002/category/1739.html</link><description>静静聆听内心世界的声音　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　
常常有令人欢喜的旋律响起




　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　可以简单、可以深沉





　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　　可以抒情、可以喧哗</description><language>zh-cn</language><lastBuildDate>Fri, 30 Sep 2011 00:05:38 GMT</lastBuildDate><pubDate>Fri, 30 Sep 2011 00:05:38 GMT</pubDate><ttl>60</ttl><item><title>NUnit写测试数据库访问层的测试类时——取不到字串</title><link>http://www.cnitblog.com/tab1002/articles/6110.html</link><dc:creator>咚咚咚</dc:creator><author>咚咚咚</author><pubDate>Tue, 10 Jan 2006 05:03:00 GMT</pubDate><guid>http://www.cnitblog.com/tab1002/articles/6110.html</guid><wfw:comment>http://www.cnitblog.com/tab1002/comments/6110.html</wfw:comment><comments>http://www.cnitblog.com/tab1002/articles/6110.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/tab1002/comments/commentRss/6110.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/tab1002/services/trackbacks/6110.html</trackback:ping><description><![CDATA[取不到字串<BR>&nbsp;&nbsp;&nbsp; string connectionString = ConfigurationSettings.AppSettings["ConnectionString"];<BR>解决：<BR>DLL和Config文件必须在一个目录； <BR>“项目名.dll”对应“项目名.dll.Config”；<BR><img src ="http://www.cnitblog.com/tab1002/aggbug/6110.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/tab1002/" target="_blank">咚咚咚</a> 2006-01-10 13:03 <a href="http://www.cnitblog.com/tab1002/articles/6110.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>单元测试和事先测试开发－Nunit</title><link>http://www.cnitblog.com/tab1002/articles/5878.html</link><dc:creator>咚咚咚</dc:creator><author>咚咚咚</author><pubDate>Thu, 29 Dec 2005 02:25:00 GMT</pubDate><guid>http://www.cnitblog.com/tab1002/articles/5878.html</guid><wfw:comment>http://www.cnitblog.com/tab1002/comments/5878.html</wfw:comment><comments>http://www.cnitblog.com/tab1002/articles/5878.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/tab1002/comments/commentRss/5878.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/tab1002/services/trackbacks/5878.html</trackback:ping><description><![CDATA[摘要：Eric Gunnerson 介绍了事先测试开发的思想，并提供了一个实用实例来演示如何在您自己的应用程序中应用它。 <BR><BR>从 MSDN Online Code Center 下载 integerlist.exe。 <BR><BR>本专栏末尾有我的简介，如果您读过的话，就会知道我在担任程序经理之前曾出任 C# 编译器的测试组长，而在此之前是 C++ 编译器的测试组长。这些工作经历使我对分析和尽量避免软件错误特别感兴趣。 <BR><BR>要减少软件中的错误数目，方法之一就是拥有一个专业的测试组，其工作就是尽一切可能使软件崩溃。不幸的是，如果拥有测试组，那么即使是经验丰富的开发人员，也会倾向于花费较少的时间来保证代码的可靠性。 <BR><BR>软件界有一句俗语：“开发人员不应该测试他们自己的代码”。这是因为开发人员对自己的代码了如指掌，他们很清楚如何采用适当的方法对代码进行测试。尽管这句俗语很有道理，但却忽略了非常重要的一点 - 如果开发人员不对自己的代码进行测试，又如何知道代码能否按照预期的方式运行？<BR><BR>简单说来，他们根本无从得知。开发人员编写那种运行不正常或只在某些情况下运行正常的代码是一个严重的问题。他们通常只测试代码能否在很少的情况下正常运行，而不是验证代码能够在所有情况下均正常运行。<BR><BR>发现软件错误<BR>发现软件错误的情况有很多： <BR><BR>由首次编写代码的开发人员发现。 <BR>由尝试运行代码的开发人员发现。 <BR>由组中的其他开发人员或测试人员发现。 <BR>作为产品大规模测试的一部分。 <BR>由最终用户发现。 <BR>如果在第一种情况下发现软件错误，则修复错误比较容易，成本也很低。情况越靠后，修复软件错误的成本就越高；修复一个由最终用户发现的软件错误可能要耗费 100 或 1000 倍的成本。更不用说用户通常因为软件错误导致工作无法继续，而一直等到下一个版本才能解决问题。<BR><BR>如果开发人员能够在编写代码期间发现所有的软件错误，那就再好不过了。为此，您必须编写能在编写代码时运行的测试。有一种很不错的方法，它恰好可以做到这一点。 <BR><BR>事先测试开发<BR>所谓的事先测试开发是指在编写代码前编写测试。如果所有测试均正常运行，便可以断定代码运行正常；添加新功能时，这些测试会继续验证您是否破坏了代码的任何部分。<BR><BR>此概念于 20 世纪 90 年代初诞生于 Smalltalk 世界，Kent Beck 在当时编写了 SmalltalkUnit。在过去的几年中，大部分环境都具备了单元测试工具，其中有一个很出色的适用于 .NET Framework 领域的工具，即 nUnit（英文）。<BR><BR>示例<BR>下面我将编写一个 IntegerList 类来介绍事先测试开发的工作原理。IntegerList 是 ArrayList 类的变体，用于在本地存储整数，因此不存在装箱和取消装箱的开销。<BR><BR>第一步是创建一个控制台项目，并向其中添加一个 IntegerList.cs 源文件。要连接 nUnit 框架，需要添加对 nUnit 框架的引用。在我的系统中，它们位于 d:\program files\nUnit v2.0\bin。 <BR><BR>第二步是花些时间考虑如何对该类进行测试。这与确定类应该具备哪些功能的过程类似，但重点放在功能的特定用途（将值 1 添加到列表并检查是否成功），而不是功能本身（将一个项目添加到列表）。要生成此类，我们首先要提供一个要使用的测试列表： <BR><BR>测试该类可以构造 <BR>将两个整数添加到列表，并确保数目和项目都正确。 <BR>执行同一操作，但针对更多的项目。 <BR>将此列表转换为一个字符串。 <BR>使用 foreach 枚举此列表。 <BR>此示例从某种程度上代表了我开始时的想法，即希望这个类执行的操作。多数类一次只会创建一小部分，测试应随着类的增长而添加。 <BR><BR>现在我可以开始了。我创建一个名为 IntegerListTest.cs 的新 C# 类文件，用于存放所有测试。下面是包含第一个测试的文件：<BR><BR>using System;<BR>using System.Collections;<BR>using NUnit.Framework;<BR><BR>namespace IntegerList<BR>{<BR>&nbsp; &nbsp; /// &lt;summary&gt;<BR>&nbsp; &nbsp; /// IntegerClassTest 的摘要说明。<BR>&nbsp; &nbsp; /// &lt;/summary&gt;<BR>&nbsp; &nbsp; [TestFixture]<BR>&nbsp; &nbsp; public class IntegerClassTest<BR>&nbsp; &nbsp; {<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;[Test]<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;public void ListCreation()<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;{<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;IntegerList list = new IntegerList();<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;Assertion.AssertNotNull(list);<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;}<BR>&nbsp; &nbsp; }<BR>}<BR><BR>[TestFixture] 属性将此类标记为测试类，[Test] 属性将 ListCreation() 方法标记为测试方法。在此方法中，我创建了一个列表，然后使用 Assertion 类测试对象 gets 已经创建。 <BR><BR>我启动 nUnit GUI 测试程序，打开可执行文件，并执行这些测试。将获得如下显示。 <BR><BR><BR>图 1：显示测试结果的 nUnit GUI<BR><BR>这表明所有测试都已通过。现在我想添加一些真实功能。第一个操作就是向列表中添加一个整数。此测试如下所示：<BR><BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;[Test]<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;public void TestSimpleAdd()<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;{<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;IntegerList list = new IntegerList();<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;list.Add(5);<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;list.Add(10);<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;Assertion.AssertEquals(2, list.Count);<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;Assertion.AssertEquals(5, list[0]);<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;Assertion.AssertEquals(10, list[1]);<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;}<BR><BR>在此测试中，我选择同时测试两个操作： <BR><BR>列表正确维护 Count 属性。 <BR>列表可以包含两个项。 <BR>某些测试驱动开发的倡议者提倡测试应尽可能只测试数目，但是如果只测试数目而不测试项目，这对于我而言有些不可思议，因此我所选择的是两者一起测试。 <BR><BR>编译这段代码时，由于 IntegerList 类中没有方法，因此编译失败，为此我加上以下代码进行编译：<BR><BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;public int Count<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;{<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;get<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;{<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; return -1;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;}<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;public void Add(int value)<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;{<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;public int this[int index]<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;{<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;get<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;{<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; return -1;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;}<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;}<BR><BR>然后我返回并运行测试，这时它们显示为红色，表示测试失败。这很好，因为它意味着测试实际上已测试出程序错误。现在我可以执行此实现。我可以做些简单的工作，尽管这样做效率不是很高：<BR><BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;public int Count<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;{<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;get<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;{<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; return elements.Length;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;}<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;public void Add(int value)<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;{<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;int newIndex;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;if (elements != null)<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;{<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; int[] newElements = new int[elements.Length + 1];<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; for (int index = 0; index &lt; elements.Length;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;index++)&nbsp; &nbsp; <BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; {<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;newElements[index] = elements[index];<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; }<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; newIndex = elements.Length;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; elements = newElements;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;}<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;else<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;{<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; elements = new int[1];<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; newIndex = 0;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;}<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;elements[newIndex] = value;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;public int this[int index]<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;{<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;get<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;{<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; return elements[index];<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;}<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;}<BR><BR>我现在已经完成类的一小部分，并已经编写了可确保其正常工作的测试，但我仅仅测试了项目中很少的一部分。接下来，我要编写一个用于检查 1000 个项的测试：<BR><BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;[Test]<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;public void TestOneThousandItems()<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;{<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;list = new IntegerList();<BR><BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;for (int i = 0; i &lt; 1000; i++)<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;{<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; list.Add(i);<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;}<BR><BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;Assertion.AssertEquals(1000, list.Count);<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;for (int i = 0; i &lt; 1000; i++)<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;{<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; Assertion.AssertEquals(i, list<I>);<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;}<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;}<BR><BR>此测试运行正常，因此无须进行任何更改。 <BR><BR>添加 ToString() 方法<BR>接下来，我将添加测试代码，以测试 ToString() 能否正常运行：<BR><BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;[Test]<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;public void TestToString()<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;{<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;IntegerList list = new IntegerList();<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;list.Add(5);<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;list.Add(10);<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;string t = list.ToString();<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;Assertion.AssertEquals("5, 10", t.ToString());<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;}<BR><BR>失败了，没关系。以下代码可以使其通过：<BR><BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;public override string ToString()<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;{<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;string[] items = new string[elements.Length];<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;for (int index = 0; index &lt; elements.Length; index++)<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;{<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; items[index] = elements[index].ToString();<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;}<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;return String.Join(", ", items);<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;}<BR><BR>启用 Foreach<BR>许多用户希望能够使用 foreach 遍历我的列表。为此，我需要在类中实现 Ienumerable，并定义一个单独的用于实现 Ienumerable 的类。第一步，测试：<BR><BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;[Test]<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;public void TestForeach()<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;{<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;IntegerList list = new IntegerList();<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;list.Add(5);<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;list.Add(10);<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;list.Add(15);<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;list.Add(20);<BR><BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;ArrayList items = new ArrayList();<BR><BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;foreach (int value in list)<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;{<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; items.Add(value);<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;}<BR><BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;Assertion.AssertEquals("Count", 4, items.Count);<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;Assertion.AssertEquals("index 0", 5, items[0]);<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;Assertion.AssertEquals("index 1", 10, items[1]);<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;Assertion.AssertEquals("index 2", 15, items[2]);<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;Assertion.AssertEquals("index 3", 20, items[3]);<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;}<BR><BR>我还通过 IntegerList 实现 IEnumerable：<BR><BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;public IEnumerator GetEnumerator()<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;{<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;return null;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;}<BR><BR>运行测试时，此代码生成异常。为了正确地实现此功能，我将使用一个嵌套类作为枚举器。 <BR><BR>&nbsp; &nbsp; class IntegerListEnumerator: IEnumerator<BR>&nbsp; &nbsp; {<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;IntegerList&nbsp; &nbsp; list;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;int index = -1;<BR><BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;public IntegerListEnumerator(IntegerList list)<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;{<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;this.list = list;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;}<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;public bool MoveNext()<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;{<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;index++;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;if (index == list.Count)<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; return(false);<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;else<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; return(true);<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;}<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;public object Current<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;{<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;get<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;{<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; return(list[index]);<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;}<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;}<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;public void Reset()<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;{<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;index = -1;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;}<BR>&nbsp; &nbsp; }<BR><BR>此类将一个指针传递给 IntegerList 对象，然后只返回此对象中的元素。<BR><BR>这样，便可以对列表执行 foreach 操作，但遗憾的是 Current 属性属于对象类型，这意味着每个值将被装箱才能将其返回。此问题可采用一种基于模式的方法加以解决，此方法酷似当前方法，但它通过 GetEnumerator() 返回一个真正的类（而非 IEnumerator），且此类中的 Current 属性为 int 类型。<BR><BR>然而执行此操作后，我要确保在不支持该模式的语言中仍然可以使用这种基于接口的方法。我将复制编写的上一个测试并修改 foreach 以转换为接口：<BR><BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;foreach (int value in (IEnumerable) list)<BR><BR>只需少许改动，列表即可在两种情况下正常运行。请查看代码样例以获取更多细节和更多测试。 <BR><BR>几点说明<BR>为本月的专栏文章编写代码和文字大约花了我一个小时的时间。事先编写测试的优点就是您可以对在类中添加哪些内容以使测试通过有一个清楚的认识，从而简化代码的编写。<BR><BR>如果要进行小型、递增的测试，则使用此方法最合适。我鼓励您在小型项目中使用此方法。事先测试开发是所谓的“敏捷方法”的一部分。有关敏捷开发的详细信息，请访问 <A href="http://www.agilealliance.com/home" target=_blank><FONT color=#003366>http://www.agilealliance.com/home</FONT></A>（英文）。 </I><img src ="http://www.cnitblog.com/tab1002/aggbug/5878.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/tab1002/" target="_blank">咚咚咚</a> 2005-12-29 10:25 <a href="http://www.cnitblog.com/tab1002/articles/5878.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>使用NUnit在.Net编程中进行单元测试</title><link>http://www.cnitblog.com/tab1002/articles/5877.html</link><dc:creator>咚咚咚</dc:creator><author>咚咚咚</author><pubDate>Thu, 29 Dec 2005 02:22:00 GMT</pubDate><guid>http://www.cnitblog.com/tab1002/articles/5877.html</guid><wfw:comment>http://www.cnitblog.com/tab1002/comments/5877.html</wfw:comment><comments>http://www.cnitblog.com/tab1002/articles/5877.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/tab1002/comments/commentRss/5877.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/tab1002/services/trackbacks/5877.html</trackback:ping><description><![CDATA[引言：<BR>举一个可能会发生在你身边的事件将更能贴近实际，幸好我们现在就有一件在程序员看来非常普通的任务：<BR>你今天第一天上班，你的项目经理拿给你一叠不算厚的文档，告诉你今天的任务是按照文档中的要求编写一个.Net类，可能因为任务并不复杂，所以他看上去非常的随意。<BR>今天能否很好的完成任务对你来说非常特殊，你拿过来后快速略过了前面大段的项目介绍，因为你知道那些对你并不重要，印象中好象是一个关于售票系统的工程。很快，你找了你需要关注的重点：类的需求说明文档。你详细的看了一遍，感觉并不复杂，类名Ticket,有一个只读的int型公共的属性，名称是Amount，还有两个方法，一个是名称是Sell，功能是将Amount减去一，表示卖掉了一张票，当然，票可不能为负数，如果是的话，抛出一个异常说明原因。另一个是Add，它有一个int型的参数，功能是将这个参数的值加到Amount中去，可能是表示进票之类的事情吧，你不太关心，反正这个程序很简单，你掩饰住内心的狂喜，打开电脑，调出编辑器，开始准备写程序了。<BR>"喂，等等"，项目经理不知道什么时候又转回来了，"我想知道你打算怎么进行单元测试，我最关心的是这个"。<BR>"什么是单元测试？"你转过头一脸沮丧的看着失望的项目经理。<BR><BR>--------------------------------------------------------------------------------<BR><BR>什么是单元测试：<BR>在程序设计过程中会有许多种测试，单元只是其中的一种，单元测试并不能保证程序是完美无缺的，但是在所有的测试中，单元测试是第一个环节，也是最重要的一个环节。单元测试是一种由程序员自行测试的工作。简单点说，单元测试就是测试代码撰写者依据其所设想的方式执行是否产生了预期的结果。关于单元测试的重要性已经有许多文章做了很多深入的分析，这里就不再赘述。<BR>NUnit是一个为Net准备的自动化单元测试框架，它的作用就是帮助你方便的完成单元测试工作，同鼎鼎有名的JUnit一样，都是xUnit家族的成员。它的下载地址是：<A href="http://www.nunit.org/" target=_blank><FONT color=#003366>http://www.nunit.org/</FONT></A>。<BR><BR>--------------------------------------------------------------------------------<BR><BR>测试先行：<BR>"什么？先写测试？"你一定非常惊讶，对！就是先来编写测试代码，按照极限编程（XP）的理论，写测试就是对软件进行设计的过程，它的重要性甚至超过了实际完成功能的代码。先将测试写完，然后再来完成代码，这样，所有的测试通过之日也就是程序完成之时。<BR>首先，我们将NUnit提供的要nunit.framework.dll文件引入到工程中，并创建一个名为TicketTest的类：<BR>[TestFixture]<BR>&nbsp; &nbsp; &nbsp; &nbsp; public class TicketTest<BR>&nbsp; &nbsp; &nbsp; &nbsp; {<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; [Test]<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; public void Add()<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Ticket ticket = new Ticket();<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ticket.Add(100);<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Assertion.AssertEquals(100, ticket.Amount);<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<BR>&nbsp; &nbsp; &nbsp; &nbsp; }<BR><BR>注意，其中的[TestFixture]和[Test]两个Attribute为NUnit所规定必须要添加的，这样，测试框架就可以知道哪些类或者方法需要进行测试。<BR>我们在Add方法中定义了一个ticket对象，并给他加了100张票，然后就可以使用：<BR>Assertion.AssertEquals(100, ticket.Amount);<BR>来测试ticket的Amount属性是否确实为100。<BR>接下来，我们再向TicketTest中添加一个测试Sell的方法：<BR>[Test]<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; public void Sell()<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Ticket ticket = new Ticket();<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ticket.Add(100);<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ticket.Sell();<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ticket.Sell();<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ticket.Sell();<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Assertion.AssertEquals(97, ticket.Amount);<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<BR><BR>这里，我们先加了100张票之后就一口气卖掉了3张，然后看看我们是否还剩下97张票。<BR>好了，这两个方法的测试已经做完了，我们来看一下测试的结果，根据要求，我们写了如下代码：<BR>public class Ticket<BR>&nbsp; &nbsp; &nbsp; &nbsp; {<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; private int amount;<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; public int Amount<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; get<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return amount;<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<BR><BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; public void Add(int num)<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<BR><BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; public void Sell()<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }<BR>}<BR><BR>注意这段代码只是为了完成类的结构，方法的实现暂时先空着。然后将这段代码编译成一个dll动态连接库文件：UnitTest.dll。<BR>我们运行NUnit的图形测试工具，打开我们编译好的dll文件，点"Run"按纽，就可以看到如下画面：<BR><BR><BR>很醒目红色，表示测试并没有成功，不过这个是在我们的预料之中的。<BR>接下来，我们向刚才的Ticket类中完成我们的Add方法实现代码：<BR>public void Add(int num)<BR>{<BR>amount += num;<BR>}<BR>保存，重新编译。<BR>切换到NUnit，再点Run，可以看到：<BR><BR><BR>Add方法已经变成绿色了，再接着将Sell方法也完成：<BR>public void Sell()<BR>{<BR>amount -= 1;<BR>}<BR>再来测试，结果就变成：<BR><BR><BR>啊，总算变成美丽的绿色了，大家现在体会到环保的重要性了吧。 ：）<BR>那么可以交任务了吗？等等，别急，还有个异常没测试呢，如果我们的Amount小于0的话，就会产生异常，那么，异常怎么测试呢？请接着看。<BR><BR>--------------------------------------------------------------------------------<BR><BR>测试异常：<BR>还是跟上面一样，先写出测试代码：<BR>[Test]<BR>&nbsp; &nbsp; &nbsp; &nbsp; [ExpectedException(typeof(Exception))]<BR>&nbsp; &nbsp; &nbsp; &nbsp; public void ExcpetionTesting()<BR>&nbsp; &nbsp; &nbsp; &nbsp; {<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Ticket ticket = new Ticket();<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ticket.Add(3);<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ticket.Sell();<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ticket.Sell();<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ticket.Sell();<BR>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ticket.Sell();<BR>&nbsp; &nbsp; &nbsp; &nbsp; }<BR><BR>其中，[ExpectedException(typeof(Exception))]表示我们希望能捕获到发生的异常，如果没有捕获到异常，则表示测试失败。<BR>后面的代码很好理解，我们加了三张票，却卖了四张出去，这可不是炒股，以后没办法平仓的。 ：）<BR>编译运行，我们看到以下的测试画面：<BR><BR><BR>在Ticket类中，我们修改一下Sell方法，让它变成：<BR>public void Sell()<BR>{<BR>if(amount - 1 &lt; 0)<BR>throw new Exception("Amount不能为0");<BR>amount -= 1;<BR>}<BR>编译，再测试，结果如下：<BR><BR><BR>好了，到了这里就算完成我们的单元测试之旅了，大家对如何在C#中进行单元测试一定已经有了一个基本的认识。另外，NUnit并不是只针对C#，事实上，你可以在任何.Net语言中使用NUnit来测试你的单元，方法都一样。<BR><BR>--------------------------------------------------------------------------------<BR><BR>总结：<BR>单元测试看上去虽然有点麻烦，但是它为程序员提供了一个安全的观点，让程序员对自己的程序更加有信心，在减少开发后期进行频繁Debug所耗费时间的同时也为应用软件提供了第一道安全防护网，因此，单元测试是提高开发效率和软件品质的一个重要的手段。<BR>利用UNint，我们可以在.Net编程过程中非常方便的进行单元测试，它图形化的界面和简单而强大的测试框架为我们提供了一个非常舒适而有趣的测试环境，能够让程序员觉得进行单元测试并不枯燥乏味，习惯后甚至还能成为一种乐趣。<BR>看完本文，如果你是引言中那个可怜的程序员的话，你现在就一定能轻松的面对你的项目经理，交上一份令人放心的代码答卷。<img src ="http://www.cnitblog.com/tab1002/aggbug/5877.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/tab1002/" target="_blank">咚咚咚</a> 2005-12-29 10:22 <a href="http://www.cnitblog.com/tab1002/articles/5877.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>NUnit快速入门</title><link>http://www.cnitblog.com/tab1002/articles/5876.html</link><dc:creator>咚咚咚</dc:creator><author>咚咚咚</author><pubDate>Thu, 29 Dec 2005 02:20:00 GMT</pubDate><guid>http://www.cnitblog.com/tab1002/articles/5876.html</guid><wfw:comment>http://www.cnitblog.com/tab1002/comments/5876.html</wfw:comment><comments>http://www.cnitblog.com/tab1002/articles/5876.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/tab1002/comments/commentRss/5876.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/tab1002/services/trackbacks/5876.html</trackback:ping><description><![CDATA[原著：NUnit v2.1<BR>原文：NUnit文档之QuickStart.doc<BR>翻译：lover_P<BR>--------------------------------------------------------------------------------<BR>[译者序]<BR>&nbsp; &nbsp; 纵观软件的开发，测试已经日益成为软件开发过程中的重要环节，通常一个软件的开发周期中测试要占到一半时间甚至更多。而在测试过程中，单元测试更是万里长征第一步，单元测试进行得是否完善，直接影响到后期集成测试的效率。进行单元测试，有许多软件可以自动完成，NUnit就是其中之一。这是一款与JUnit齐名的，同属于xUnit家族的单元测试软件（在<A href="http://www.nunit.org/" target=_blank><FONT color=#003366>http://www.nunit.org/</FONT></A>我们可以免费得到这款软件）。<BR><BR>[正文]<BR><BR>&nbsp; &nbsp; 让我们从一个简单的例子开始。假设我们正在编写一个银行应用程序，而我们有一个这一领域的基本类——Account。Account支持存款、取款和资金转帐。这个Account类看起来会是这个样子：<BR><BR>namespace bank {<BR>&nbsp; &nbsp; public class Account {<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;private float balance;<BR><BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;public void Deposit(float amount) {<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;balance += amount;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;public void Withdraw(float amount) {<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;balance -= amount;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;public void TransferFunds(Account destination, float amount) {<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;public float Balance {<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;get {<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; return balance;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;}<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;}<BR>&nbsp; &nbsp; }<BR>}<BR><BR>&nbsp; &nbsp; 现在我们来为这个类写一个测试——AccountTest。我们要测试的第一个类方法是TransferFunds。<BR><BR>namespace bank {<BR>&nbsp; &nbsp; using NUnit.Framework;<BR><BR>&nbsp; &nbsp; [TestFixture]<BR>&nbsp; &nbsp; public class AccountTest {<BR><BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;[Test]<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;public void TransferFunds() {<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;Account source = new Account();<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;source.Deposit(200.00F);<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;Account destination = new Account();<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;destination.Deposit(150.00F);<BR><BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;source.TransferFunds(destination, 100.00F);<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;Assert.AreEqual(250.00F, destination.Balance);<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;Assert.AreEqual(100.00F, source.Balance);<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;}<BR>&nbsp; &nbsp; }<BR>}<BR><BR>&nbsp; &nbsp; 首先要注意的是这个类关联了一个[TestFixture]特性（attribute）——这表示这个类包含了测试代码（这个特性可以被继承）。这个类必须是公有的，但他的父类并不受限制。这个类还必须有一个默认构造函数。<BR><BR>&nbsp; &nbsp; 类中唯一的一个方法——TransferFunds()，关联了一个[Test]特性——这表示它是一个测试方法。测试方法的返回值必须为void并且不能带有参数。在我们的测试方法中，我们对被测试的对象进行了一般的初始化，执行了被测试的方法并检查了对象的状态。Assert类定义了一组方法用于检查给定的条件，在我们的例子中我们使用了AreEqual()方法来确保交易过后两个账户都有正确的余额（这个方法有很多重载，我们在这个例子中使用的版本带有两个参数：第一个参数是我们的期望值，第二个参数是实际值）。<BR><BR>&nbsp; &nbsp;<FONT color=#ff1493> <STRONG>编译并运行这个例子。假设你已经将你的测试代码编译为bank.dll。打开NUint Gui（安装程序会在你的桌面和“程序”菜单中建立一个快捷方式），打开GUI后，选择File-&gt;Open菜单项，找到你的bank.dll并在“Open”对话框中选中它。</STRONG></FONT>bank.dll装载后你会在左边的面板中看到一个测试树结构，还有右边的一组状态面板。单击Run按钮，状态条和测试树种的TransferFunds节点变成了红色——我们的测试失败了。“Errors and Failures”面板显示如下消息——“TransferFunds: expected &lt;250&gt; but was &lt;150&gt;”，在它正下方的堆栈跟踪面板报告了测试失败的语句在代码中的位置——“at bank.AccountTest.TransferFunds() in C:\nunit\BankSampleTests\AccountTest.cs:line 17”<BR><BR>&nbsp; &nbsp; 这正是预期的结果，因为我们还未实现TransferFunds()方法。现在我们来搞定它。不要关闭GUI，回到你的IDE并修改代码，使你的TransferFunds()方法看起来像这样：<BR><BR>public void TransferFunds(Account destination, float amount) {<BR>&nbsp; &nbsp; destination.Deposit(amount);<BR>&nbsp; &nbsp; Withdraw(amount);<BR>}<BR><BR>&nbsp; &nbsp; 现在重新编译你的代码并再次在GUI中点击Run按钮——状态条和数节点变绿了。（注意GUI会自动地为你重新加载程序集；我们可以一直开着GUI而在IDE中继续工作并写更多的测试）。<BR><BR>&nbsp; &nbsp; 让我们来为我们的Account的代码添加一些错误检测。为账户添加一个最小余额限制，通过你的最小透支保护费来维持它的持续运作。首先我们来为Account类添加一个最小余额保护属性：<BR><BR>private float minimumBalance = 10.00F;<BR><BR>public float MinimumBalance {<BR>&nbsp; &nbsp; get {<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;return minimumBalance;<BR>&nbsp; &nbsp; }<BR>}<BR><BR>&nbsp; &nbsp; 我们使用一个异常来指出透支：<BR><BR>namespace bank {<BR>&nbsp; &nbsp; using System;<BR><BR>&nbsp; &nbsp; public class InsufficientFundsException : ApplicationException {<BR>&nbsp; &nbsp; }<BR>}<BR><BR>&nbsp; &nbsp; 向我们的AccountTest类添加一个新的方法：<BR><BR>[Test]<BR>[ExpectedException(typeof(InsufficientFundsException))]<BR>public void TransferWithInsufficientFunds() {<BR>&nbsp; &nbsp; Account source = new Account();<BR>&nbsp; &nbsp; source.Deposit(200.00F);<BR>&nbsp; &nbsp; Account destination = new Account();<BR>&nbsp; &nbsp; destination.Deposit(150.00F);<BR>&nbsp; &nbsp; source.TransferFunds(destination, 300.00F);<BR>}<BR><BR>&nbsp; &nbsp; 这个测试方法除了[Test]特性之外还关联了一个[ExpectedException]特性——这指出测试代码希望抛出一个指定类型的异常；如果在执行过程中没有抛出这样的一个异常——该测试将会失败。编译你的代码并回到GUI。由于你编译了你的测试代码，GUI会变灰并重构了测试树，好像这个测试还没有被运行过（GUI可以监视测试程序集的变化，并在测试树结构发生变化时进行更新——例如，添加了新的测试）。点击“Run”按钮——我们又一次得到了一个红色的状态条。我们得到了下面的失败消息：“TransferWithInsufficentFunds: InsufficientFundsException was expected”。我们来再次修改Account的代码，象下面这样修改TransferFunds()方法：<BR><BR>public void TransferFunds(Account destination, float amount) {<BR>&nbsp; &nbsp; destination.Deposit(amount);<BR><BR>&nbsp; &nbsp; if(balance - amount &lt; minimumBalance)<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;throw new InsufficientFundsException();<BR><BR>&nbsp; &nbsp; Withdraw(amount);<BR>}<BR><BR>&nbsp; &nbsp; 编译并运行测试——绿了。成功！不过等等，看看我们刚写的代码，我们会发现银行在每一笔不成功的转账操作时都亏钱了。让我们来写一个测试来确认我们的猜测。添加这个测试方法：<BR><BR>[Test]<BR>public void TransferWithInsufficientFundsAtomicity() {<BR>&nbsp; &nbsp; Account source = new Account();<BR>&nbsp; &nbsp; source.Deposit(200.00F);<BR>&nbsp; &nbsp; Account destination = new Account();<BR>&nbsp; &nbsp; destination.Deposit(150.00F);<BR><BR>&nbsp; &nbsp; try {<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;source.TransferFunds(destination, 300.00F);<BR>&nbsp; &nbsp; }<BR>&nbsp; &nbsp; catch(InsufficientFundsException expected) {<BR>&nbsp; &nbsp; }<BR><BR>&nbsp; &nbsp; Assert.AreEqual(200.00F,source.Balance);<BR>&nbsp; &nbsp; Assert.AreEqual(150.00F,destination.Balance);<BR>}<BR><BR>&nbsp; &nbsp; 我们测试了方法的交易属性——是否所有的操作都成功了。编译并运行——红条。是的，我们平白无故地损失了300块钱——source账户有正确的余额150.00，但destination账户显示：$450.00。我们该如何修改？我们能够只将最小余额检查的调用放到数据更新的前面么：<BR><BR>public void TransferFunds(Account destination, float amount) {<BR>&nbsp; &nbsp; if(balance - amount &lt; minimumBalance) {<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;throw new InsufficientFundsException();<BR>&nbsp; &nbsp; }<BR>&nbsp; &nbsp; destination.Deposit(amount);<BR>&nbsp; &nbsp; Withdraw(amount);<BR>}<BR><BR>&nbsp; &nbsp; 如果Withdraw()方法抛出了另外一个异常呢？我们应该在catch块中执行一个补救处理，还是依赖我们的交易管理器来重新装载对象的状态？某些时候我们必须回答这样的问题，但不是现在；可我们眼前如何应付这个失败的测试呢——删除它？一个不错的方法是临时忽略它在你的测试方法中添加下面的特性：<BR><BR>[Test]<BR>[Ignore("Need to decide how to implement transaction management in the application")]<BR>public void TransferWithInsufficientFundsAtomicity() {<BR>&nbsp; &nbsp; // code is the same<BR>}<BR><BR>&nbsp; &nbsp; 编译并运行——黄条。单击“Test Not Run”选项卡，你会看到bank.AccountTest.TransferWithInsufficientFundsAtomicity()连同这个测试被忽略的原因一起列在列表中。<BR><BR>&nbsp; &nbsp; 看看我们的测试代码，我们可以看到一些适宜的重构。所有的方法共享一组公共的测试对象。让我们来将这些初始化代码放到一个setup方法中并在所有的测试中重用它们。我们的测试类的重构版本像下面这样：<BR><BR>namespace bank {<BR>&nbsp; &nbsp; using System;<BR>&nbsp; &nbsp; using NUnit.Framework;<BR><BR>&nbsp; &nbsp; [TestFixture]<BR>&nbsp; &nbsp; public class AccountTest {<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;Account source;<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;Account destination;<BR><BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;[SetUp]<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;public void Init() {<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;source = new Account();<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;source.Deposit(200.00F);<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;destination = new Account();<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;destination.Deposit(150.00F);<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;[Test]<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;public void TransferFunds() {<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;source.TransferFunds(destination, 100.00f);<BR><BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;Assert.AreEqual(250.00F, destination.Balance);<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;Assert.AreEqual(100.00F, source.Balance);<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;[Test]<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;[ExpectedException(typeof(InsufficientFundsException))]<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;public void TransferWithInsufficientFunds() {<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;source.TransferFunds(destination, 300.00F);<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;}<BR><BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;[Test, <BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;Ignore (<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;"Need to decide how to implement transaction management in the application"<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;)]<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;public void TransferWithInsufficientFundsAtomicity() {<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;try {<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; source.TransferFunds(destination, 300.00F);<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;}<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;catch(InsufficientFundsException expected) {<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;}<BR><BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;Assert.AreEqual(200.00F,source.Balance);<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;Assert.AreEqual(150.00F,destination.Balance);<BR>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;}<BR>&nbsp; &nbsp; }<BR>}<BR><BR>&nbsp; &nbsp; 注意这个初始化方法拥有通用的初始化代码，它的返回值类型为void，没有参数，并且由[SetUp]特性标记。编译并运行——同样的黄条！<img src ="http://www.cnitblog.com/tab1002/aggbug/5876.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/tab1002/" target="_blank">咚咚咚</a> 2005-12-29 10:20 <a href="http://www.cnitblog.com/tab1002/articles/5876.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>