·您的位置: 首页 » 资源教程 » 编程开发 » ASP.NET » 网上发现的文章(测试驱动开发)

网上发现的文章(测试驱动开发)

类别: ASP.NET教程  评论数:0 总得分:0

??介

腠然由程式檫办人?T自己??Unit Tests(?卧??y??)???y??自己??的程式瘁已??行之有年,但是大部分的Unit Tests都是??在主要的程式瘁已??韵??好、??好之後。大部分的程式檫办人?T都有相同的的????,在主要程式瘁??好之後再?砑尤胝nit Test是一??困膣的工作,而且在?r殓的?毫χ?下Unit Test通常是第一??被跳咿的步笈。

呃篇文章要介铰的Test-driven development (TDD,?y?????娱_办方法),其主要目的就是???D要解?Q呃一????铨,?K且??程式檫办人?T可以因此??出更高品冱的,完整?y??咿的程式瘁。其方法就是把整??程序反弈咿?恚?在??主要程式瘁之前就要把Unit Tests??好。TDD是所洲的Extreme Programming(XP,剿?O程式??作)彦面所提到主要Practices(???铡???作)之一,在Java???I中?裼迷DD的程式檫办人?T???挡簧伲?在.NET的???I中?t??只停留在很少?档奈恼抡?到如何使用TDD。

何?檎nit Tests(?卧??y??)?

根??Ron Jeffries(XP???I大??)的真法,所洲Unit Tests就是「…杂多段的程式,??呃些程式的目的是用?沓膳??绦楔run in batch mode),以??酌我??所??的Classes(???e)。每一??Unit Test都????送一??Message斤一??特定的Class,?K且??酌所?骰??淼闹凳窃?Test所盍期的答案。」如果我??用比蒉???盏恼f法?碚f明的??,呃段??的意思就是,你??一??程式,你用呃??程式???y??在你的主要程式中所有的Classes的Public Interfaces(Public介面)。Unit Tests跟所洲的Requirement Tests或Acceptance Tests不同,Unit Test?y??的重?c在於??酌你所??的Methods(子程序)所?a生的劫果,陪你所盍期的一模一?印</P>

呃??真?砗??危?做起?砜赡芴??鹦院芨摺J紫龋?你必??先要?Q定用什?N工具????呃些Unit Tests。以往?y??人?T通常使用一些很大型、妖塍的test engine(?y??引擎),配合一些妖塍的scripting languages(倪本遮言、?⑹鲂猿淌秸Z言)????呃些Unit Tests。呃??可能只唔合???I的?y??人?T或?y??部樵使用,??於由程式檫办人?T自己??的Unit Tests?碚f,就不那?N唔用了。事??上??於一般的程式韵??人?T?碚f,他??所需要的是一套的toolkit(工具酵,程式?飑,??他??可以使用他??原本在程式檫办咿程就已??熟知的程式遮言及IDE(檫办工具)????出呃些Unit Tests?怼</P>

大部分呃些年流行的Unit Testing Frameworks(Unit Test檫办框架)都是源自於由Kent Beck(XP ??始人) 所韵??的Unit Test Framework。呃??Unit Test Framework的背景是?樗?洲第一??XP?0辅Chrysler C3?0俯所特?e韵??的。呃??最起初的Framework是用Smalltalk??成,?K且???v咿多次的改版之後,到今天都??存在。在呃??Smalltalk版本的Framework之後,Kent和Erich Gamma(Design Pattern迷????知道他是侦)又把呃??Framework斤改版到Java上,?K且正式命名叫作jUnit。?拇酥?後,呃??Framework就檫始不?啾桓陌妗??K??用到各??不同的程式遮言之上,其中包括了C++、VB、Python、Perl、以及杂多不同的程式遮言。

NUnit Framework(NUnit ?卧??y??框架)??介

本文所????的NUnit 2.0是一??陪它的先祖??(其他的Framework)非常不一?拥陌姹尽F渌?的xUnit家族版本通常都有一??base class(基盗???e),你要??的test classes(?y?????e)都得inherit(擂承)自呃??base class。除此之外,?e?o他法能?蜃?你??Unit Tests。不幸的是,呃??很多的程式遮言?碚f就造成很大的限制。比如真,Java及C#就只能允杂single inheritance(?我焕^承)。也就是真,如果你想要refactor(重整)你的Unit Tests程式瘁的??,你??遭遇到一些的限制;除非你引咄一些妖塍的inheritance hierarchies(???e擂承?蛹?)。

有了.NET之後一切又不同了,.NET引咄了一??新的程式檫办的概念 ─ Attributes(?傩冤,解?Q了呃???┤说???铨。Attributes??你可以在你的程式瘁之上再加入metadata(後韵儋料/母儋料/超儋料,描述程式瘁的儋料)。一般?碚fAttributes不??影??到主要程式瘁的?绦校?其功能是在你所??程式瘁之上添加了铪外的儋??。Attributes主要使用在documenting your code(暂解你的程式瘁),但是Attributes也可以用?硖峁┯嘘PAssembly的铪外儋??,其他的程式就算?]有??咿呃??Assembly,也可以使用呃些儋??。呃基本上就是NUnit 2.0所作的事。在NUnit 2.0彦面,有一??Test Runner Application(?????绦姓nit Tests的程式),呃??Test Runner???呙枘阋呀?compile(??诅)好的程式瘁,?K且?牧ttribute彦面知道哪些classes是test classes,哪些methods是需要?绦械聂est methods. 然後,Test Runner使用.NET的Reflection技戌???绦羞@些test methods。因?檫@????故,你就不再需要??你的test classes擂承自所洲的common base class。你唯一需要作的事,就是使用正催的Attribute?砻枋瞿愕聂est classes及test methods。
NUnit提供了杂多不同的attributes,??你可以自由的??你想要的unit tests。呃些attributes可以用?矶?柳test fixtures(??下一段解??)、test methods,以及setup及teardown的methods(盍?浼吧漆峁ぷ鞯捻ethods)。除此之外,??有其他的attributes可以?碓O定盍期办生的exceptions,或者要求Test Runner跳咿某些test method不?绦小</P>

TestFixture Attribute??介

TestFixture attribute主要是用在class上,其作用的?苏I??class含有需要?绦械聂est methods。??你在一??class的定柳彦加上呃??attribute,Test Runner就???z查??class,看看呃??class是否含有test methods。

底下呃段程式瘁示??了如何使用TestFixture Attribute。(本文中所有程式瘁都是用C#??成,但是你????知道,NUnit也是用於其他的.NET程式遮言,包括VB.NET。???⒁?NUnit的相晷文件。)

namespace UnitTestingExamples
{
    using System;
    using NUnit.Framework;

    [TestFixture]
    public class SomeTests
    {
    }
}

使用TextFixture Attribute的class需要符合另一??唯一附加的限制,就是需要有一??public的default constructor(或者是?]有定柳任何的constructor,呃其??是相同的意思)。

Test Attribute??介

Test attribute主要用???耸驹隰ext fixture中的method,表示呃??method需要被Test Runner application所?绦小S性est attribute的method必??是public的,?K且必??return void,也?]有任何?魅氲????怠H绻??]有符合呃些??定,在Test Runner GUI之中是不??列出呃??method的,而且在?绦姓nit Test的?r候也不???绦羞@??method。

底下的程式瘁示??了使用呃??attribute的方法:

namespace UnitTestingExamples
{
    using System;
    using NUnit.Framework;

    [TestFixture]
    public class SomeTests
    {
        [Test]
        public void TestOne()
        {
            // Do something...
        }
    }
}

SetUp & Teardown Attributes??介

在??Unit Tests的?r候,有?r你??需要在?绦忻恳???test method之前(或之後)先作一些盍?浠蛏漆峁ぷ鳌.?然,你可以??一??private的method,然後在每一??test method的一檫钷或最末端呼叫呃??特?e的method。或者,你可以使用我??要介铰的SetUp及Teardown Attributes?磉_到相同的目的。

如同呃????Attributes的名字的意思,有Setup Attribute的method??在??TextFixture中的每一??test method被?绦兄?前先被Test Runner所?绦校?而有Teardown Attribute的method?t??在每一??test method被?绦兄?後被Test Runner所?绦小R话??碚f,Setup Attribute及Teardown Attribute被用?眍A?湟恍┍仨?的objects(物件),例如database connection、等等。

底下的??例示??了如何使用呃????attributes:

namespace UnitTestingExamples
{
    using System;
    using NUnit.Framework;

    [TestFixture]
    public class SomeTests
    {
        private int _someValue;

        [SetUp]
        public void Setup()
        {
            _someValue = 5;
        }

        [TearDown]
        public void TearDown()
        {
            _someValue = 0;
        }

        [Test]
        public void TestOne()
        {
            // Do something...
        }
    }
}

ExpectedException Attributes??介

有的?r候,你希望你的程式在某些特殊的?l件下???a生一些特定的exception。要用Unit Test???y??程式是否如盍期的?a生exception,你可以用一??try..catch的程式?^段?磴atch(捕捉)呃??exception,然後再韵一??boolean的值?碜C明exception的催办生了。呃??方法固然可行,但是太花偻功夫。事??上,你????使用呃??ExpectedException attribute???耸灸???method?????a生哪一??exception,如同下面的??例所示:

namespace UnitTestingExamples
{
    using System;
    using NUnit.Framework;

    [TestFixture]
    public class SomeTests
    {
        [Test]
        [ExpectedException(typeof(InvalidOperationException))]
        public void TestOne()
        {
            // Do something that throws an InvalidOperationException
        }
    }
}

如果上面的程式被?绦械??r候,如果一旦exception办生,而且呃??exception的type(儋料型?e)是InvalidOperationException 的??,呃??test就????利通咿??酌。如果你盍期你的程式瘁???a生多??exception的??,你也可以一次使用多??ExpectedException attribute。但是,一??test method????只?y??一件事情,一次?y??多??功能是不好的做法,你??????量避免之。另外,呃??attributes?K不???z查inheirtance的晷?S,也就是真,如果你的程式瘁?a生的exception是擂承自InvalidOperationException 的subclass(子???e)的??,呃??test?绦械??r候?⒉???通咿??酌。??而言之,??你使用呃??attribute的?r候,你要明催的指明所盍期的exception是哪??type(儋料型?e)的。

Ignore Attributes??介

呃??attribute你大概不????常用的,但是一但需要的?r候,呃??attribute是很方便使用的。你可以使用呃??attribute???耸灸???test method,叫Test Runner在?绦械??r候,略咿呃??method不要?绦小J褂眠@??Ignore attribute的方法如下:

namespace UnitTestingExamples
{
    using System;
    using NUnit.Framework;

    [TestFixture]
    public class SomeTests
    {
        [Test]
        [Ignore(\"We\'re skipping this one for now.\")]
        public void TestOne()
        {
            // Do something...
        }
    }
}

如果你想要???r性的comment out一??test method的??,你????考?]使用呃??attribute。呃??attribute??你保留你的test method,在Test Runner的?绦薪Y果彦面,也??提醒你呃??被略咿的test method的存在。

NUnit Assertion Class??介

除了以上所提到的呃些用???耸??y??程式所在的attributes之外,NUnit??有一??重要的class你????要知道如何使用。呃??class就是Assertion class。Assertion class提供了一系列的static methods,??你可以用?眚?酌主要程式的劫果陪你所盍期的是否一?印5紫碌墓?例示??了如何使用Assertion class:

namespace UnitTestingExamples
{
    using System;
    using NUnit.Framework;

    [TestFixture]
    public class SomeTests
    {
        [Test]
        public void TestOne()
        {
            int i = 4;
            Assertion.AssertEquals( 4, i );
        }
    }
}

(我知道呃段程式瘁只是用?硎竟?用的,但是呃段程式????很明白的示??了我的意思。)

?绦心愕脑ests

好,?F在我??已??????咿??Unit Tests的基本步笈及方法,?F在??我???砜纯慈绾??绦心闼???的Unit Tests。事??上非常???巍NUnit彦面有????已????好的Test Runner applications:一??是??窗GUI程式,一??是console XML(命令列)程式。你可以自由啉?衲闼?喜?g的方式,基本上是?]有什?N差?e的。
如果你要使用??窗GUI的Test Runner app,你只需要?绦性?程式,然後告灾它你要?绦械聂est method所在的assembly位置。呃??包含有你所??test methods的assembly是那一??class library(或是executable,*.dll或*.exe) assembly,其中含有前面??到的Test Fixtures。??你告灾Test Runner你的assembly所在的位置,Test Runner??自?屿oad呃??asembly,然後把所有的class及test methods都列在??窗的左?凇.?你按下’Run’按嫔?r,你就??自???绦兴?有列出?淼聂est methods。你也可以double click其中的一??test class,或是一??test method之上,呃????自?又??绦性?class或是??method。
底下是??窗GUI Test Runner?绦??r的?幼樱?

在一些的情?r下,特?e是你想要在你自己??的build script中加入Unit Testing的情?r下,你大概不??使用GUI Test Runner。在呃??自???绦锈uild script的情?r下,你一般??把你build的劫果傥在咀??,或??入log file彦面存作硷??,以供程式檫办人?T、??理或是客?艨梢越逵??z查呃??硷??知道????情?r。
在呃??情?r,你可以用NUnit 2.0的console Test Runner application。呃??Test Runner可以?魅脶ssembly的位置?????担?其?y???绦薪Y果是一??XML字串。你可以用XSLT或是CSS把呃??XML劫果弈?Q成HTML,或是其他你想要的格式。如果你需要用到呃??功能的??,??查看NUnit文件中有晷console Test Runner application的儋料。

使用Test-Driven Development(?y?????娱_办方法)

真了呃?N多,你已??知道怎?N??Unit Tests了,?Π桑堪⒐?,跟??程式一?樱?只知道遮法,距滕可以??出好的程式??有一大段距滕的。你??需要?W??一些的技巧及方法,??你可以真正??出???I水?实???用程式出?怼5紫挛???????到一些?椭?你檫始的技巧及方法,但是你必??知道,唯一能?蜃?你??出?蛩??实恼nit Tests的方法只有一??,????、????、再????。

如果你完全?]有??咿TDD的??,底下所真的?|西你大概一?r之殓???o法接受。以往杂多的程式韵??人?T,花了?o?档??r殓及???v,??了杂多的??籍及文章,告灾我??在??程式瘁之前要好好的做韵??的功夫,然後才是??出程式瘁,最後?t是小心地?y??你??的程式是否正催。好了,忘掉呃一切吧,我接下?硪?告灾你的,是??上面所真的完全背道而褓的流程。

我??不再先韵??、再??程式瘁、再作?y??,我??整??把呃??流程反弈咿?恚?先???y??瘁。用另外的方式?碚f,我??睫?Σ???任何一行的主要程式,除非我??先??了?y??瘁,先?绦辛唆est methods,先有了一????????不通咿??酌的?y??。也就是真,整????程式的流程??????成像呃???幼樱?

  1. 先??一段Unit Test。

  2. ?绦羞@??Unit Test。??然呃??Test呗compile(??诅)都不能compile,因?槟愀?本都???]有??任何的主要程式瘁。 (我??把呃??也??作Test?]有通咿)

  3. ??你所能想到最???蔚某淌酱a,??你的Test可以compile。

  4. ?F在再次?绦心愕恼nit Test,你??????得到??酌失?〉慕Y果。 (如果不幸通咿了的??,表示你的Unit Test根本?]有?糁幸?害,你的Unit Test是??不?蚝玫恼nit Test) 。

  5. ?F在你可以??出你的主要程式,??你的Unit Test可以??利的通咿??酌。

  6. 再?绦心愕恼nit Test,?F在你的Unit Test????已????利通咿了。 (如果??是不通咿,你????回到第5??,?z查看看你的程式瘁哪彦出邋,修正之,然後再?绦姓nit Test) 。

  7. ?F在你可以回到第1步笈,檫始??新功能的Unit Test!

事??上,??你在第5步笈的?r候,你所用????程式瘁的方法,就正好是所洲的Coding by Intention(目?恕⒁??D??向)的方法。所洲的Coding by Intention就是真,你??程式瘁是由上而下??的。其相?Φ姆椒?就是由下而上的??法,也就是真,??你??一段程式的程式的?r候,如果你办?F你正在??的class需要另一??A class提供一??Foo method,呃?r你就先跳到A class去把呃??method??好,然後再回咿钷????你之前正在??的class。Coding by Intention?t正好相反,??你??程式的?r候,你假砚A class已??有你所需要的Foo method。等到你??完你的程式要compile的?r候,你的程式工具??????告灾你你少了一??class或是一??method,呃?r候才?砑尤脒@??所需要的method。如同我??之前所真的,呃是一件好事情,程式?o法compile跟你的Unit Test不通咿??酌是同?拥囊换厥隆</P>

??你用Coding by Intention的?r候,你很清楚的用程式遮言表哌你想要作的事。呃不但是?椭?我????好Unit Tests,也??我??所??的程式更加的清楚、容易明白、容易debug(除邋),程式的韵??也??更加的完善。在?鹘y程式檫办方法中,?y??只是用???椭?我????酌我??所??的程式?]有邋锗。但是在TDD彦面,Unit Tests可以?椭?我??在??程式瘁之前,先清楚的定柳我??所要的是什?N,我??的class????要有哪些的功能。我睫不是真用TDD??比用?鹘y的?y??方法??要蒺??容易,但是我的????告灾我,其?a生的劫果的催?Τ淌介_办有?O大的助益。

如果你已????咿,也坐咿有晷Extreme Programming的??,下面的程式瘁?δ??碚f只是妖??你已??知道的知滓。如果TDD和Extreme Programming?δ??碚f??很陌生,你可以看一下以下的??例。假韵你要??一??程式,呃??程式可以??你的使用者存遑在他的泫行???粞e面。?F在,根??TDD的原?t,??我????我??的BankAccount class之前,我??????先?恼nit Test著手。首先,我??先????我??的BankAccountTests class,我第一??想到的是我的BankAccount class????可以接受存款?K且告灾我新的鹞铪有多少。底下就是我的Unit Test程式瘁:

namespace UnitTestingExamples.Tests
{
    using System;
    using NUnit.Framework;

    [TestFixture]
    public class BankAccountTests
    {
        [Test]
        public void TestDeposit()
        {
            BankAccount account = new BankAccount();
            account.Deposit( 125.0 );
            account.Deposit( 25.0 );
            Assertion.AssertEquals( 150.0, account.Balance );
        }
    }
}

?F在我??好了,我先???Dcompile。哈,不能compile,??然,我都???]有??我的BankAccount class 呢。?F在你知道,呃就是Test Driven Development的基本原?t:除非你有一??不通咿??酌的Unit Test,否?t不要??任何的程式瘁。??然,不能compile也算是不通咿??酌。

?F在我可以????我的BankAccount class,我只有??我所能想到最???巍⒆铌?春的程式瘁,其目的只是??我的Unit Test可以被compile:

namespace UnitTestingExamples.Library
{
    using System;

    public class BankAccount
    {
        public void Deposit( double amount )
        {
        }

        public double Balance
        {
            get { return 0.0; }
        }
    }
}

YES,呃次compile已??咿晷了,接下?砦揖??碛迷est Runner?绦形业恼nit Test。嗯,?]有通咿,Test Runner告灾我\"TestDeposit: expected: <150> but was <0>\"。(诅者言,我在此不翻诅呃??error message,??竟呃是你????要看得懂的,除非你用的是中文版的NUnit,否?t?W??看懂error message是必要的)。?F在,接下?砦揖鸵?????一些程式瘁??我的Unit Test能?蛘嬲?通咿??酌,?K?a生我想要的劫果:

namespace UnitTestingExamples.Library
{
    using System;

    public class BankAccount
    {
        private double _balance = 0.0;

        public void Deposit( double amount )
        {
            _balance += amount;
        }

        public double Balance
        {
            get { return _balance; }
        }
    }
}

OK,?F在我的Unit Test咿晷了,我可以咄行下一步的工作。(诅者言,事??上根??Extreme Programming或是Kent Beck??上的真法,你????要先作Refactoring(重整)的工作)。

使用Mock Objects(模?M物件) ?C DotNetMock

??你在??Unit Test的?r候,你??碰到一些挑?穑?其中一??就是要催保每一??test method都只有???蔚??y??一???我坏墓δ堋5?是,在一般的情?r之下,你的test method所要?y??的功能,往往??依??其他的objects才能???绦衅涔δ堋,F在,如果你的test method?y??呃??功能,你真正?y??的不只是呃??功能,你也?y??了另外的class。

如果呃是一????铨,你可以用Mock Objects???椭?你?^隔出你真正想要?y??的功能。所洲的Mock Object,其主要的功能就是模?M?e的Object,??你可以?y??呃一??被模?M的object是不是有被如盍期般的正常使用。更要撅的是,使用Mock Objects??有以下的好??:

  1. 很容易就可以??好

  2. 很容易就可以盍?浜媚阋?的儋料

  3. ?绦兴俣??O快

  4. ?a生的劫果是可盍期的

  5. 可以??你??酌某??object是否正催的呼叫了??呼叫的method,以及是否依照正催的??序?砗艚羞@些methods。

下面的程式??例示??了一??典型的mock object使用例子。??注意,?F在Unit Test??得更加清晰,更加容易??人明白,而且?绦羞@??test只???y??了我??想?y??的程式瘁,而不???砍兜讲幌喔傻娘bjects。

namespace UnitTestingExamples.Tests
{
    using DotNetMock;
    using System;

    [TestFixture]
    public class ModelTests
    {
        [Test]
        public void TestSave()
        {
            MockDatabase db = new MockDatabase();
            db.SetExpectedUpdates(2);

            ModelClass model = new ModelClass();
            model.Save( db );

            db.Verify();
        }
    }
}

如上所示,使用呃??MockDatabase class的盍?涔ぷ骱苋菀祝?也??我??很容易的就可以??酌是否Save呃??method呼叫了MockDatabase class。使用了呃??mock object也??我??不需要操心真正的儋料?焓欠襁/作正常。我??唯一知道的,就是??呃??ModelClass object?绦杏ave呃???幼鞯??r候,它?炔???呼叫Database物件的Update method?纱巍N???的Unit Test就是要??酌是否真的有呼叫Update method?纱巍K?以我??先告灾MockDatabase object我??盍期Update method??被呼叫?纱危?然後我???绦许odel.Save(),最後??酌其劫果。因?橥ockDatabase不?可娴竭B劫儋料?斓???铨,我??也就不需要??心儋料????因???绦??y??被修改,或是如何盍?溆行У??y??儋料,…此??的??铨。我??唯一需要晷心的是,Save method是否真的造成了Update被呼叫?纱巍</P>

\"When Mock Objects are used, only the unit test and the target domain code are real.\" -- Endo-Testing: Unit Testing with Mock Objects by Tim Mackinnon, Steve Freeman and Philip Craig.

(??你使用Mock Objects的?r候,只有你的Unit Test以及你所要?y??的程式瘁是真??的?|西。 ─ 引自Endo-Testing一文,作者Tim Mackinnon, Steve Freeman and Philip Craig)

如何?y??Business Layer

?y??你的Business Layer(企?I?????萤程式瘁,是一般程式檫办人?T在嘱到Unit Test的?r候最常佩的例子。如果你小心地????及韵??你的Business Layer,你的Business Layer????是loosely coupled(陪其他部分??立)以及highly cohesive(?炔烤o密呗劫的)。就???丈系挠谜Z?碚f,所洲的coupling指的是你的class陪其他的classes互相依??的程度,如果coupling的程度越低的??,如果我??要修改其中某??class的??,我??就不需要??心其他的class的功能??被影??到。?牧硪唤嵌??砜矗?所洲的cohrsive就是真你的class????只?????我坏娜??眨?不????加入其他的不完全相干的功能。

如果你的Business layer class library(企?I?????映淌??飑是loosely coupled以及highly conhesive的??,要??Unit Test????是蒺而易佩的事。你????可以???γ恳???business class(在Business Layer彦的class)都??一??Unit Test class,?K且,你????可以蒺??的???γ恳???business class的public methods都??出相????的?y???怼</P>

?α耍?如果你办迂??你想要???δ愕拟usiness class??Unit Test?r,?s办?F困膣重重,你也杂要考?]作一些大格局的Refactoring(重整)的工作。??然,如果你是先??Unit Tests再??business classes的??,你????是不可能??落入到呃??地步的。

如何?y??User Interface(使用者介面)

??你著手想要??User Interface(使用者介面)的程式瘁的?r候,有一些??铨就檫始跑出?砹恕.?然你可以想揠法??你的User Interface符合loosely coupled的要求,??它陪其他的class都?]有依??晷?S。但是,所洲的User Interface本冱上就是??依??於使用者,需要由使用者?眚??蛹膀?酌。那?N,到底要如何的?斫??Q呃????铨,??我??可以?y??我??的User Interface呢?

呃??答案的晷嫔之??在於,我??????要小心的把所洲的logic(程式?绦械倪???),陪所洲的user interface(用?盹@示儋料)清楚的?^分檫?恚???User Interface真正只????锢示view(??界/愚?c)的工作。有很多的pattern(韵??模式)就是用???椭?我??做好呃??工作,它??的名费不蓖相同(Model-View-Controller, Model-View-Presenter, Doc-View,等等),但是都是同?拥哪康摹_@些pattern的??始者都深切的篦帐到,把view及view所要作事的????(也就是controller)?^分檫?恚?是一件很有助益的事。

那?N,我??要如何使用呃些pattern???椭?我????Unit Test???y??User Interface呢?我在呃彦所示??的技巧是?碜造锻ichael Feathers所??的The Humble Dialog Box一文。就其本冱上?碚f,呃??技巧就是??你的view class(也就是User Interface class)Implement(??作)一?????蔚纳nterface。呃??Interface定柳了呃??view class????要锢示的儋料,?K且有getting/setting methods(存取儋料的metods)。你的view class???????蔚闹挥胸???把儋料锢示斤使用者看,??使用者作了一些?幼??r(例如按了一??按嫔),view class彦的event handler的??任就是???蔚陌堰@???幼鹘唤ocontroller?硖?理。

我??用一????例?砜矗?就??更加的清楚明白。假韵我??要??一??程式,其中一????窗是要??使用者嫔入自己的名字及Social Security Number(社安??瘁,??似身分酌字??诅暂)。呃?????谖欢急仨?催??填??,所以我??需要?z查使用者是否??入名字,以及社安??瘁是否符合正催的格式。既然我??????要先???y??瘁,我??就要遵守自己的???t:

[TestFixture]
public class VitalsControllerTests
{
    [Test]
    public void TestSuccessful()
    {
        MockVitalsView view = new MockVitalsView();
        VitalsController controller = new VitalsController(view);

        view.Name = \"Peter Provost\";
        view.SSN = \"123-45-6789\";

        Assertion.Assert( controller.OnOk() == true );
    }

    [Test]
    public void TestFailed()
    {
        MockVitalsView view = new MockVitalsView();
        VitalsController controller = new VitalsController(view);

        view.Name = \"\";
        view.SSN = \"123-45-6789\";
        view.SetExpectedErrorMessage( controller.ERROR_MESSAGE_BAD_NAME );
        Assertion.Assert( controller.OnOk() == false );
        view.Verify();

        view.Name = \"Peter Provost\";
        view.SSN = \"\";
        view.SetExpectedErrorMessage( controller.ERROR_MESSAGE_BAD_SSN );
        Assertion.Assert( controller.OnOk() == false );
        view.Verify()

    }
}

如果你???D要compile及build呃段?y??程式瘁,你??看到一大堆的邋锗??息。?e撅??,呃是因?槲???都???]有檫始??MockVitalsView以及VitalsController呃????class的??故。好吧,?F在就??我??????呃????classes的原始架??。??得我??的???t,我??只要??最???巍⒖梢糟ompile就好的程式瘁:

public class MockVitalsView
{
    public string Name
    {
        get { return null; }
        set { }
    }

    public string SSN
    {
        get { return null; }
        set { }
    }

    public void SetExpectedErrorMessage( string message )
    {
    }

    public void Verify()
    {
        throw new NotImplementedException();
    }
}

public class VitalsController
{
    public const string ERROR_MESSAGE_BAD_SSN = \"Bad SSN.\";
    public const string ERROR_MESSAGE_BAD_NAME = \"Bad name.\";

    public VitalsController( MockVitalsView view )
    {
    }

    public bool OnOk()
    {
        return false;
    }
}

咻!?F在我??的Unit Test已??成功的compile了,??我???碓???看是否呃些Unit Tests可以通咿??酌。噗!Test Runner告灾我??有????tests?]有通咿??酌。第一??是在在TestSuccessful 彦面呼叫controller.OnOk的?r候,因???骰??淼慕Y果是false,而非我??所盍期的true。第二??地方是在TestFailed彦面呼叫view.Verify的?r候。

我??擂理遵照我??先???y??瘁的原?t?碜鳎??F在我??需要的是想揠法??我??的Unit Tests可以??利通咿。如果只是要??TestSuccessful 通咿就很???危?但是要??TestFailed 也通咿我??就必??要??一些真正有用的程式瘁了。比如真像呃?樱?

public class MockVitalsView : MockObject
{
    public string Name
    {
        get { return _name; }
        set { _name = value; }
    }

    public string SSN
    {
        get { return _ssn; }
        set { _ssn = value; }
    }

    public string ErrorMessage
    {
        get { return _expectedErrorMessage.Actual; }
        set { _expectedErrorMessage.Actual = value; }
    }

    public void SetExpectedErrorMessage( string message )
    {
        _expectedErrorMessage.Expected = message;
    }

    private string _name;
    private string _ssn;
    private ExpectationString _expectedErrorMessage =
                            new ExpectationString(\"expected     error message\");
}

public class VitalsController
{
    public const string ERROR_MESSAGE_BAD_SSN = \"Bad SSN.\";
    public const string ERROR_MESSAGE_BAD_NAME = \"Bad name.\";

    public VitalsController( MockVitalsView view )
    {
        _view = view;
    }

    public bool OnOk()
    {
        if( IsValidName() == false )
        {
            _view.ErrorMessage = ERROR_MESSAGE_BAD_NAME;
            return false;
        }

        if( IsValidSSN() == false )
        {
            _view.ErrorMessage = ERROR_MESSAGE_BAD_SSN;
            return false;
        }

        // All is well, do something...

        return true;
    }

    private bool IsValidName()
    {
        return _view.Name.Length > 0;
    }

    private bool IsValidSSN()
    {
        string pattern = @\"^/d{3}-/d{2}-/d{4}$\";
        return Regex.IsMatch( _view.SSN, pattern );
    }

    private MockVitalsView _view;
}

在我??擂理下去之前,??我??先?和R幌禄仡^看看呃段程式瘁。首先要注意的是,我??完全?]有更?拥轿???起初??的Unit Test程式(呃是?槭颤N我?]有把它??列在上面的原因)。我??所大幅更?拥模?是在MockVitalsView 以及VitalsController之上。我??先?砜赐ockVitalsView。

在我??先前的例子彦面,MockVitalsView原本?]有擂承自任何的base class。?F在?榱撕?化我??的工作,我??把它??成擂承自DotNetMock.MockObject。呃一??MockObject class斤我??一??Verify method,呃??method?槲???作了所有的工作。它所?碛械纳衿媪α磕耸??碜造跺xpectation classes 。我??使用expectation class?碓O定我??所盍期mock object???a生的事。在呃??例子彦,我??盍期ErrorMessage呃??Property??有一??特定的值。因?檫@??Property是?凫队tring儋料型?B的,所以我??在我??的mock object彦面加入了一??叫做ExpectationString的member。接下?砦?????了SetExpectedErrorMessage 以及ErrorMessage 呃????method及property,??它??使用呃??ExpectationString object。??我??在我??的?y??瘁中呼叫Verify的?r候,MockObject base class就??自?拥??z查我??所盍期的劫果是否??办生,?K且通知我??任何陪盍期不一致的地方。很酷吧。是不是啊?

另外一??被大幅更?拥木褪俏???的VitalsController class。因?檫@是所有真正出力?只畹某淌酱a所在,我??知道大幅更?幽耸窃谒?膣免。基本上,我??主要的????,主要的程式瘁都是在OnOK呃??method彦面。在呃彦,我??用了我??的view class所定柳存取儋料的method?碜x取使用者所填??的儋料,然後如果儋料有任何不符合的??,我??用ErrorMessage呃??property?戆彦e锗??息?骰厝ァ</P>

所以,我??的工作已??告一段落?樱窟?早的咧。到目前?橹梗?我??都只是在controller上面大配土木,我??只是用模?M的mock view?砑傺b我??有一??User Interface。事??上,我?????]有任何?|西可以秀斤使用者看呢! ?e撅??,我???F在需要的就只是??我??已????好的controller,可以呗接到真??的view上面去。怎?N做呢?

首先,我??需要把MockVitalsView所需要implement的Interface斤抓出?怼H绻?我??看一下VitalsController 以及VitalsControllerTests 的程式瘁,我??就可以办?F底下的呃??interface????就???M足我??的需要:

public interface IVitalsView
{
    string Name { get; set; }
    string SSN { get; set; }
    string ErrorMessage { get; set; }
}

我??有了我??要的新interface,?F在,我??可以把在controller彦面用到MockVitalsView的程式瘁,改成使用IVitalsView。然後,我??就可以修改MockVitalsView,??它 implements呃??IVitalsView。??然,我??做了呃??Refactoring的?幼髦?後,要隗快的斤它?绦幸幌挛???的Unit Test,催保我???]有作了任何的傻事。假韵一切都正常,所有的?y??都通咿了,我??就可以真正檫始????view了。在呃??例子彦面我用了ASP.NET的咀???碜魑???的view,你????知道,你可以很容易的就??一??Windows Form的view。

底下是我??的.ASPX ?n案:

<%@ Page language=\"c#\" Codebehind=\"VitalsView.aspx.cs\"
    AutoEventWireup=\"false\"
    Inherits=\"UnitTestingExamples.VitalsView\" %>
<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" >

<html>
<head>
    <title>VitalsView</title>
    <meta name=\"GENERATOR\" Content=\"Microsoft Visual Studio 7.0\">
    <meta name=\"CODE_LANGUAGE\" Content=\"C#\">
    <meta name=vs_defaultClientScript content=\"JavaScript\">
    <meta name=vs_targetSchema content=\"http://schemas.microsoft.com/intellisense/ie5\">
</head>
    <body MS_POSITIONING=\"GridLayout\">
    <form id=\"VitalsView\" method=\"post\" runat =\"server\">
    <table border=\"0\">
    <tr>
        <td>Name:</td>
        <td><asp:Textbox runat =server id=nameTextbox /></td>
    </tr>
    <tr>
        <td>SSN:</td>
        <td><asp:Textbox runat =server id=ssnTextbox /></td>
    </tr>
<tr>
        <td> </td>
        <td><asp:Label runat =server id=errorMessageLabel /></td>
</tr>
<tr>
        <td> </td>
        <td><asp:Button runat =server id=okButton Text=\"OK\" /></td>
</tr>
</table>
</form>
</body>
</html>
 

底下是ASP.NET的code-behind程式瘁:

using System;
using System.Web.UI.WebControls;
using UnitTestingExamples.Library;

namespace UnitTestingExamples
{
    /// <summary>
    /// Summary description for VitalsView.
    /// </summary>
    public class VitalsView : System.Web.UI.Page, IVitalsView
    {
        protected TextBox nameTextbox;
        protected TextBox ssnTextbox;
        protected Label errorMessageLabel;
        protected Button okButton;

        private VitalsController _controller;

        private void Page_Load(object sender, System.EventArgs e)
        {
            _controller = new VitalsController(this);
        }

        private void OkButton_Click( object sender, System.EventArgs e )
        {
            if( _controller.OnOk() == true )
                Response.Redirect(\"ThankYou.aspx\");
        }

        #region IVitalsView Implementation

        public string Name
        {
            get { return nameTextbox.Text; }
            set { nameTextbox.Text = value; }
        }

        public string SSN
        {
            get { return ssnTextbox.Text; }
            set { ssnTextbox.Text = value; }
        }

        public string ErrorMessage
        {
            get { return errorMessageLabel.Text; }
            set { errorMessageLabel.Text = value; }
        }

        #endregion

        #region Web Form Designer generated code
        override protected void OnInit(EventArgs e)
        {
            //
            // CODEGEN: This call is required by the ASP.NET Web Form Designer.
            //
            InitializeComponent();
            base.OnInit(e);
        }

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.Load += new System.EventHandler(this.Page_Load);
            okButton.Click += new System.EventHandler( this.OkButton_Click );
        }
        #endregion
    }
}

如你所??的,唯一在我??的程式瘁彦??所需的,就是implement我??之前所提到的IVitalsView 呃??interface。 ?K且把我??的的ASP.NET Web Controls呗劫到IVitalViews所定柳的存取儋料的methods。??然,我??需要催保我??的view class彦面有一??controller object,?K且呼叫controller彦面的methods。如果我??用呃??方法????User Interface,你??办?F??View的咿程??得非常容易。而且因?樗?有的工作都在controller彦面完成,你可以?y??到你大部分的程式瘁,你???δ愕某淌椒浅5挠行判摹</P>

劫??

Test-Driven Development是一??很有威力的工具,你????立刻褚上就使用呃??工具?硖??N你程式的品冱,以及你程式??作的功力。使用TDD????你更加仔??思考你的程式是否韵??完善,?K且保酌所有你的程式瘁都是??咿?y??千垂百??的(诅暂:我承帐我在此掌??了?c)。如果你?]有完善的Unit Test,要???δ悻F有的程式瘁作Refactoring是?缀醪豢赡艿氖隆R坏?跳入TDD的洪流之中,睫大部分的人都不再回钷用以前的老方法??程式。????看,你??办?F,嗯~,真的是粉棒的啦。




-= 资 源 教 程 =-
文 章 搜 索
关键词:
类型:
范围:
纯粹空间 softpure.com
Copyright © 2006-2008 暖阳制作 版权所有
QQ: 15242663 (拒绝闲聊)  Email: faisun@sina.com
 纯粹空间 - 韩国酷站|酷站欣赏|教程大全|资源下载|免费博客|美女壁纸|设计素材|技术论坛   Valid XHTML 1.0 Transitional
百度搜索 谷歌搜索 Alexa搜索 | 粤ICP备19116064号-1