我還記得當(dāng)我第一次得到自動(dòng)測(cè)試的 bug 時(shí)的情況。在一次大會(huì)上,當(dāng)我做完叫做 Bitter Java 的演講之后,Mike Clark(Java 社區(qū)的自動(dòng)測(cè)試大師,性能調(diào)整工具 JUnitPerf 的作者(請(qǐng)參閱 參考資料),現(xiàn)在是 Ruby on Rails 專(zhuān)家)走近我。Mike 告訴我有一種方法可以通過(guò)自動(dòng)測(cè)試改進(jìn)我的演講。在那次大會(huì)的剩余時(shí)間里,我跟著他四處走,看到了我能看到的盡可能多的他的測(cè)試會(huì)議。我開(kāi)始使用他推薦的技術(shù),并對(duì)把紅條(代表測(cè)試失。┳兂删G條(代表測(cè)試通過(guò))上了癮。自動(dòng)測(cè)試改變了我思考軟件開(kāi)發(fā)的方式。
關(guān)于本系列
在 跨越邊界 系列中,作者 Bruce Tate 提出了這樣一個(gè)觀點(diǎn):如今的 Java 程序員可以通過(guò)學(xué)習(xí)其他方法和語(yǔ)言得到很好的其他思路。自從 Java 明顯成為所有開(kāi)發(fā)項(xiàng)目的佳選擇以來(lái)編程前景已經(jīng)改變。其他的框架正影響構(gòu)建 Java 框架的方式,從其他語(yǔ)言學(xué)到的概念可以影響您的 Java 編程。您編寫(xiě)的 Python(或 Ruby、Smalltalk ... )代碼可以改變您處理 Java 編碼的方式。
本系列為您介紹與 Java 開(kāi)發(fā)根本不同,但也可以直接應(yīng)用于 Java 開(kāi)發(fā)的編程概念和技術(shù)。在一些例子中,需要對(duì)技術(shù)進(jìn)行集成以利用它。在另外一些例子中,您將能夠直接應(yīng)用這些概念。單獨(dú)的工具不及其他語(yǔ)言和框架能夠影響 Java 社區(qū)中的開(kāi)發(fā)人員、框架甚至基本方法的思想那么重要。
Java 社區(qū)有自動(dòng)測(cè)試的 bug。坦白地說(shuō),我們別無(wú)選擇。競(jìng)爭(zhēng)壓力迫使許多公司編寫(xiě)越來(lái)越多的代碼,而測(cè)試人員越來(lái)越少,同時(shí)每個(gè)開(kāi)發(fā)人員的又必須有更高的生產(chǎn)率。如果不進(jìn)行自動(dòng)測(cè)試,得到測(cè)試的內(nèi)容會(huì)更少,面對(duì)現(xiàn)代應(yīng)用程序不斷增長(zhǎng)的復(fù)雜性,較少的測(cè)試不是一個(gè)可行的選擇方案。
在過(guò)去十年中,我們已經(jīng)看到了對(duì)測(cè)試工具和技術(shù)的研究。JUnit 和 TestNG 都是支持自動(dòng)單元測(cè)試的工具,而且由日常的開(kāi)發(fā)人員所驅(qū)動(dòng)。Selenium 是改進(jìn)集成和功能測(cè)試的工具。一套稱(chēng)作敏捷技術(shù) 的新開(kāi)發(fā)過(guò)程告訴人們要更加重視自動(dòng)測(cè)試,不要太多地依賴(lài)正式的設(shè)計(jì)工具,將它們作為提高質(zhì)量的惟一工具。Java 社區(qū)已經(jīng)走了很長(zhǎng)的路。 (請(qǐng)參閱 參考資料,獲得這里討論的工具與技術(shù)的附加信息。)
其他編程社區(qū)也有 bug 工具, 其中一些社區(qū)使用的自動(dòng)測(cè)試要比 Java 開(kāi)發(fā)人員還有多,他們使用自動(dòng)測(cè)試經(jīng)驗(yàn)有完全不同的原因:
Smalltalk 程序員使用自動(dòng)測(cè)試已經(jīng)幾乎有 30 年的時(shí)間了,所以通過(guò)動(dòng)態(tài)類(lèi)型化語(yǔ)言使用的一些技術(shù)更加先進(jìn)。
集成框架的開(kāi)發(fā)人員的優(yōu)勢(shì)是了解框架元素的結(jié)構(gòu)和組合。有些框架,例如 Ruby on Rails,能夠生成測(cè)試用例,而且在默認(rèn)情況下提供測(cè)試特性。
具有高級(jí)元編程(metaprogramming)能力的語(yǔ)言,例如 Ruby and Lisp,允許使用其他語(yǔ)言不支持的一些測(cè)試技巧,例如更容易訪(fǎng)問(wèn) mock 對(duì)象。
在這一篇和下一篇文章中,將全面理解在 Ruby on Rails 集成開(kāi)發(fā)框架中的測(cè)試方式。第 1 部分側(cè)重于測(cè)試模型對(duì)象,并提供一些從 Rails 獲得啟發(fā)的策略,可以用這些策略使 Java 單元測(cè)試更有效。第 2 部分把更多時(shí)間花在功能測(cè)試和集成測(cè)試上。作為 Java 程序員,您對(duì)一些概念可能比較熟悉,特別是在測(cè)試的時(shí)候,而其他一些概念可以拓展您的理解。
補(bǔ)漏
在這個(gè)系列的 前一期 中,了解了動(dòng)態(tài)類(lèi)型化會(huì)帶來(lái)某些 bug 種類(lèi),靜態(tài)類(lèi)型化語(yǔ)言將在編譯時(shí)捕捉到這些 bug。清單 1 的 Ruby 代碼片段包含四個(gè)不同的 bug,這四個(gè) bug 在運(yùn)行時(shí)之前都不會(huì)顯露出來(lái):
清單 1. 帶 bug 的 Ruby 代碼
position = "2" #string, where a number was intended
position = positoin + 4 #position is misspelled, evaluates to 0
puts "The position is:" +
position.to_string #The method should be to_s
如果編譯器能夠捕捉 bug,那么這類(lèi) bug 解決起來(lái)是小菜一碟,但是如果依賴(lài)解釋器,那么管理這些 bug 困難得多。為了處理這些微妙的錯(cuò)誤,動(dòng)態(tài)語(yǔ)言的用戶(hù)長(zhǎng)期以來(lái)一直依賴(lài)于自動(dòng)測(cè)試。在進(jìn)行測(cè)試的時(shí)候,比起其他語(yǔ)言,動(dòng)態(tài)語(yǔ)言及其集成環(huán)境在一般意義和特殊意義上都具有顯著的優(yōu)勢(shì):
語(yǔ)言更簡(jiǎn)潔。測(cè)試基本上是腳本編程,許多好的腳本語(yǔ)言都是動(dòng)態(tài)類(lèi)型化的。
集成環(huán)境支持的假設(shè)可以讓集成測(cè)試更容易,也可能更強(qiáng)大。在 Rails 環(huán)境中將看到一些示例。
動(dòng)態(tài)語(yǔ)言允許使用更松散的耦合,使一些測(cè)試格式更容易實(shí)現(xiàn)。
在了解動(dòng)態(tài)語(yǔ)言開(kāi)發(fā)人員為什么這么熱衷于測(cè)試之后,現(xiàn)在是構(gòu)建一個(gè)需要一些真正測(cè)試的實(shí)際應(yīng)用程序的時(shí)候了。
構(gòu)建一個(gè)快速 Rails 應(yīng)用程序
為了進(jìn)展得快些,我采用了一個(gè)保存山地摩托車(chē)路線(xiàn)數(shù)據(jù)庫(kù)的 Rails 應(yīng)用程序。我將模型的幾個(gè)測(cè)試放在一起。如果想和我一起編寫(xiě)代碼,那么所有需要的工具是一個(gè)數(shù)據(jù)庫(kù)引擎(我使用的是 MySQL)和 Ruby on Rails 1.1 或更新版本(請(qǐng)參閱 參考資料)。第一步是創(chuàng)建 Rails 項(xiàng)目。在命令提示符下輸入 rails trails 命令,清單 2 顯示了命令和結(jié)果:
清單 2. 構(gòu)建 Rails 應(yīng)用程序
> rails trails
create
create app/controllers
create app/helpers
create app/models
create app/views/layouts
...partial results deleted...
create test/fixtures
create test/functional
create test/integration
create test/mocks/development
create test/mocks/test
create test/unit
create test/test_helper.rb
...partial results deleted...
create config/environment.rb
create config/environments/production.rb
create config/environments/development.rb
create config/environments/test.rb
...partial results deleted...
create log/server.log
create log/production.log
create log/development.log
create log/test.log