概述
單元測試
單元測試是一個白盒測試,一般是針對一個方法單元進行的測試,單元測試要求運行快,編寫簡單。所以一般單元測試有這么一些特質:
不連接數據庫
不訪問磁盤文件
不訪問遠程網絡
能夠在很短時間內運行完畢(比如三秒內)
集成測試
集成測試可以稱之為灰盒測試,是指對系統中幾個模塊集成起來進行測試。有很多時候,單元測試無法發現的問題,通過集成測試能發現。
功能測試是黑盒測試,完全從系統的功能邊界外,不對系統內部做任何假設,就以用戶的方式對系統進行使用測試。功能測試運行是最慢的,編寫和維護都非常困難。但是對交付成功的軟件的價值確實最大的。
測試能給我們帶來收益,但是測試也需要我們投入很多精力。如何能在投入和收益間找到平衡,就需要我們在系統中結合這三種測試。一般,系統中單元測試最多,集成測試次之,最少的是功能測試。這就是經典的測試三角。
對自動化測試的誤解
這是對測試最常見的誤解,測試不能幫助我們防止bug,寫了很多測試,并不意味著我們的系統就不會出現bug。自動化測試是防止我們在演進系統的過程中,引入新的bug。因為人工的測試非常緩慢,如果我們修改了代碼,需要等待很長時間才能知道我們的修改是否正確,這就會給修改代碼帶來很大的風險,有可能我們就不等待人工測試的結果就交付產品。如果我們有一套能很快運行的自動化測試集,我們就能更快的驗證我們的修改是否對已有功能造成破壞,是否引入了新的bug。認識到這一點,那就要調整一下心態:有可能有人看到長期以來,測試也沒有幫住我們什么,就不愿意寫測試,覺得不值得。我覺得這是好事,那說明我們開發能力很高,很少出現bug。如果bug很多,自動化測試并沒有起到防止引入bug的作用,我們就應該調研,如何寫質量更高的測試。
總結:自動化測試是一種快速反饋的手段。
測試代碼不是交付的一部分
很多人認為,我們編寫的產品代碼才是我們要交付給最終用戶的東西,而測試代碼不是。所以我們可以以隨便的態度編寫自動化測試代碼,在產品代碼中應用的一些最佳實踐或原則,我們可以不應用到測試代碼中。這個觀點也是錯誤的,不維護的自動化測試,比沒有自動化測試更有危害。
第一,不維護的自動化測試很難跟隨產品代碼同演進,可能已經喪失了他原有的作用,而我們心里卻以為我們有自動化測試,但實際上那只是擺設,只會誤導我們。
第二,如果需求改變了,我們需要修改測試,讓它體現最新的需求,但是因為測試年久失修,變得很難看懂,很難修改。那么最大的可能就是:刪除測試代碼,那么我們花在自動化測試上的時間就付諸東流了。只在最后階段運行測試
其實這一條和第一條有相似之處,很多人認為既然是測試,那么就應該在測試階段跑,而我們現在在開發產品代碼,這是開發階段,我們不應該運行測試。按照第一條最后的總結,自動化測試是一種反饋手段,那么我們更應該持續不斷地,頻繁的運行測試。當我們對代碼有修改時,就運行測試;當發現測試失敗后,立即修復,以免將這些債留到后期,那個時候修復的成本將更高昂。
實踐
以可測試性為目標來編寫代碼
即使我們不采用測試驅動開發(TDD)的方式開發產品,但我們也應該將可測試性這條準則銘記在心。你編寫的代碼無法自動化測試,或難以自動化測試就必須依靠人工手動測試。人工測試會有疏漏,而且重復兩次的測試可能有些少許的不同,都會疏漏一些重要的問題,或者難以重現bug。
比如我們要盡量避免與一些難以測試的部分緊耦合,比如連接數據庫部分,向屏幕打印輸出的部分,發送短信的部分等。比如你在一個類里,需要發送郵件,現在你需要測試這個類里的邏輯,這個時候你并不是為了測試發送郵件的客戶端。如果你這個時候直接使用發送郵件客戶端的靜態方法,這就是一種非常強的緊耦合,如果我們想自動化測試這個類,我們就無法用花費更小,運行更快的單元測試甚至是集成測試來測試這段代碼,我們就必須在功能測試這個層次來測試。
測試應該可以重復運行
第一個測試運行完畢后,修改了第二個測試所依賴的一個關鍵數據,測試運行不了了,必須人工干涉測試才能繼續進行。這種自動化測試也是沒有任何價值的。
幸好,我們有事務這個好東西,在事務開始時我們準備數據,然后運行測試。在測試完畢后,我們回滾事務,沒有污染數據庫。就拿基于Spring的集成測試為例:
Integration.java
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations="classpath:applicationContext-test.xml")
@TransactionConfiguration(transactionManager = "txManager") @Transactional public abstract class IntegrationTest { }
我們添加@Transactional標簽,讓我們的測試在事務中運行。為了避免所有的測試類,都要帶上那堆標簽,為此提取一個基類?;愒O為抽象的是因為,一個沒有任何測試的測試類,maven運行這類測試的時候會報錯誤。
如果在某一個測試中我們不想讓事務回滾,我們可以添加@Rollback(false)標簽(這一般在調試時很有用)。
AgentServiceIntegrationTest.java
public class AgentServiceIntegrationTest extends IntegrationTest{ @Before public void setUp(){ } @Test public void should_find_agent_by_id(){ } @Rollback(false) @Test public void we_want_to_store_data_in_database(){ } @After public void tearDown(){ } }
何時添加自動化測試
調試
當你打開調試器,一步一步的調試代碼來找出那個糾結的問題時,為什么不添加一個自動化測試來幫你定位問題呢。很多時候,這類調試甚至要啟動一個tomcat才能進行。
原文轉自:http://www.anti-gravitydesign.com