日期:2008-08-29  浏览次数:20421 次

簡介

雖然由程式開發人員自己寫Unit Tests(單元測試)來測試自己寫的程式碼已經行之有年,但是大部分的Unit Tests都是寫在主要的程式碼已經設計好、寫好之後。大部分的程式開發人員都有相同的的經驗,在主要程式碼寫好之後再來加入Unit Test是一項困難的工作,而且在時間的壓力之下Unit Test通常是第一個被跳過的步驟。

這篇文章要介紹的Test-driven development (TDD,測試驅動開發方法),其主要目的就是試圖要解決這一個問題,並且讓程式開發人員可以因此寫出更高品質的,完整測試過的程式碼。其方法就是把整個程序反轉過來,在寫主要程式碼之前就要把Unit Tests寫好。TDD是所謂的Extreme Programming(XP,終極程式寫作)裡面所提到主要Practices(實務、實作)之一,在Java陣營中採用TDD的程式開發人員為數不少,在.NET的陣營中則還只停留在很少數的文章談到如何使用TDD。

何為Unit Tests(單元測試)?

根據Ron Jeffries(XP陣營大將)的說法,所謂Unit Tests就是「…許多段的程式,寫這些程式的目的是用來成批執行(run in batch mode),以驗證我們所寫的Classes(類別)。每一個Unit Test都負責送一個Message給一個特定的Class,並且驗證所傳回來的值是該Test所預期的答案。」如果我們用比較實務的說法來說明的話,這段話的意思就是,你寫一個程式,你用這個程式來測試在你的主要程式中所有的Classes的Public Interfaces(Public介面)。Unit Tests跟所謂的Requirement Tests或Acceptance Tests不同,Unit Test測試的重點在於驗證你所寫的Methods(子程序)所產生的結果,與你所預期的一模一樣。

這個說來簡單,做起來可能挑戰性很高。首先,你必須先要決定用什麼工具來寫這些Unit Tests。以往測試人員通常使用一些很大型、複雜的test engine(測試引擎),配合一些複雜的scripting languages(腳本語言、敘述性程式語言)來寫這些Unit Tests。這個可能只適合專業的測試人員或測試部門使用,對於由程式開發人員自己寫的Unit Tests來說,就不那麼適用了。事實上對於一般的程式設計人員來說,他們所需要的是一套的toolkit(工具組,程式庫),讓他們可以使用他們原本在程式開發過程就已經熟知的程式語言及IDE(開發工具)來寫出這些Unit Tests來。

大部分這些年流行的Unit Testing Frameworks(Unit Test開發框架)都是源自於由Kent Beck(XP 創始人) 所設計的Unit Test Framework。這個Unit Test Framework的背景是為所謂第一個XP專案(Chrysler C3專案)所特別設計的。這個最起初的Framework是用Smalltalk寫成,並且經歷過多次的改版之後,到今天都還存在。在這個Smalltalk版本的Framework之後,Kent和Erich Gamma(Design Pattern迷應該知道他是誰)又把這個Framework給改版到Java上,並且正式命名叫作jUnit。從此之後,這個Framework就開始不斷被改版、並應用到各個不同的程式語言之上,其中包括了C++、VB、Python、Perl、以及許多不同的程式語言。

NUnit Framework(NUnit 單元測試框架)簡介

本文所討論的NUnit 2.0是一個與它的先祖們(其他的Framework)非常不一樣的版本。其他的xUnit家族版本通常都有一個base class(基礎類別),你要寫的test classes(測試類別)都得inherit(繼承)自這個base class。除此之外,別無他法能夠讓你寫Unit Tests。不幸的是,這對很多的程式語言來說就造成很大的限制。比如說,Java及C#就只能允許single inheritance(單一繼承)。也就是說,如果你想要refactor(重整)你的Unit Tests程式碼的話,你會遭遇到一些的限制;除非你引進一些複雜的inheritance hierarchies(類別繼承層級)。

有了.NET之後一切又不同了,.NET引進了一個新的程式開發的概念 ─ Attributes(屬性),解決了這個煩人的問題。Attributes讓你可以在你的程式碼之上再加入metadata(後設資料/母資料/超資料,描述程式碼的資料)。一般來說Attributes不會影響到主要程式碼的執行,其功能是在你所寫程式碼之上添加了額外的資訊。Attributes主要使用在documenting your code(註解你的程式碼),但是Attributes也可以用來提供有關Assembly的額外資訊,其他的程式就算沒有見過這個Assembly,也可以使用這些資訊。這基本上就是NUnit 2.0所作的事。在NUnit 2.0裡面,有一個Test Runner Application(負責執行Unit Tests的程式),這個Test Runner會掃描你已經compile(編譯)好的程式碼,並且從Attribute裡面知道哪些classes是test classes,哪些methods是需要執行的test methods. 然後,Test Runner使用.NET的Reflection技術來執行這些test methods。因為這個緣故,你就不再需要讓你的test classes繼承自所謂的common base class。你唯一需要作的事,就是使用正確的Attribute來描述你的test classes及test methods。
NUnit提供了許多不同的attributes,讓你可以自由的寫你想要的unit tests。這些attributes可以用來定義test fixtures(見下一段解釋)、test methods,以及setup及teardown的methods(預備及善後工作的methods)。除此之外,還有其他的attributes可以來設定預期發生的exceptions,或者要求Test Runner跳過某些test method不執行。

TestFixture Attribute簡介

TestFixture attribute主要是用在class上,其作用的標誌該class含有需要執行的test methods。當你在一個class的定義裡加上這個attribute,Test Runner就會檢查該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,這其實是相同的意思)。