譯者導讀:本文分爲三部分,第一部分是第1節,即說明「對數據庫測試的根本誤解」;第二部分從第2節至倒數第4節,詳述「數據庫測試測什麼」的問題;第三部分是最後3節,引出「數據庫測試怎麼測」的問題,提出自動部署、自動化測試、持續集成的思路及工具。另,副標題是俺自行添上去的,以明示本文意圖。 程序員
有許多關於測試驅動開發(Test-Driven Development,縮寫爲TDD)的書籍。那些書一般關注的是將測試應用於工做單元(units of work)。對於工做單元的理解有許多種不一樣的方式,一般它表示一個類(class)。正如那些書中所言:編寫許多測試,以使那些測試都能經過的方式建立代碼。應模擬全部的外部資源,以便你能夠只測試這個單元。 數據庫
這很酷,但不幸的是全部的測試在此刻中止了。由於一般會有些沒被測到的查詢(手寫的或者是由某些ORM工具生成的)。有些程序員使用集成測試來測試那些查詢——鏈接到一個真實的數據庫並執行真實的查詢來進行測試。這種作法一般意味着在測試快樂路徑(happy path)——我已經有了ORM工具,因此它會搞定每件事,我無須費心。 網絡
數據庫一般是一家公司最有價值的資產。應用程序能夠一遍一遍重寫。舊的應用程序扔出去,新的應用程序裝進來。可是更換應用程序時沒人會丟棄滿載數據的數據庫。而是將數據庫當心翼翼地遷移過去。位於多個系統中的許多不一樣的應用程序會在同一時刻使用同一數據庫。這就是爲何擁有充滿約束的良好數據庫模型是如此重要、以及爲何應謹慎對待數據庫的緣由。你真的不想破壞數據一致性(consistency),由於這會使你的公司付出高昂的代價。 session
本文是關於常常被遺忘的數據庫測試的。使用真實數據進行集成測試。實際上,它與你所使用的數據庫引擎的類型可有可無。你可使用PostgreSQL、MySQL、Oracle,或者甚至使用那些有趣的noSQL數據庫,例如MongoDB。如下規則可適用於各類數據庫和各種應用程序。也許不是所有,例如noSQL數據庫就沒法強制實施數據完整性(integrity)。 架構
你的應用程序一般是由許多不一樣的部件組成的。其中有一些<將任何你喜歡的語言放在這裏>代碼,一些配置文件,一些SQL查詢,一些外部系統。測試一個應用程序意味着分別測試每一個部件(由於只有這樣才更容易找出bug)、以及測試全部部件是如何協做的。數據庫就是這些部件的其中之一,並且你應該完全測試它。 app
這是首要的、最可怕的錯誤。根本不測試數據庫。你編寫了一些使用數據庫的代碼。你甚至使用一些模擬數據庫鏈接爲這些類建立了單元測試。 dom
集成測試怎麼樣?集成測試應在生產環境下對應用程序進行測試。集成測試背後的惟一想法是,確保應用程序部署到生產環境後能夠正常工做。若是你不在生產數據庫上測試應用程序,那麼你實際上不併不知道應用程序可否工做。你的模擬鏈接讓你能夠發送還沒有檢查以及沒有檢查的任何查詢。模擬鏈接只返回你所需的數據。 數據庫設計
不建立集成測試意味着你實際上沒有測試你的應用程序。 wordpress
我所觀察過的大多數團隊擁有某種形式的集成測試。一般進行快樂路徑測試:有某個ORM工具,咱們持久化對象,ORM工具會完成那些工做,真是太酷了,我無須費心。 工具
我從未見過一支對數據庫schema(模式/架構)進行測試的團隊。想象一下,因爲某些針對產品的查詢很慢,所以你必須在該數據庫中建立某個索引。當下次在新的客戶環境中部署此應用程序時,你但願擁有該索引並確認該索引真的就在那裏。爲何不編寫一個簡單的測試來檢查該索引的存在呢?
除了索引,還有許多要測試的內容:
當你開發某個應用程序時,你能夠從種類繁多的數據庫中進行選擇。一般你會從中選擇那個最好的、那個被團隊所熟知的、或者是由管理層所選定的(有時使用一些奇怪的理由)。有時同一應用程序的多個部署會在同一時間使用不一樣的數據庫引擎。有時應用程序會爲了能使用不一樣的數據庫引擎進行準備,所以購買此應用程序的客戶就能夠選擇他想要的數據庫。
數據庫引擎的選擇真的與進行產品測試無關。
因爲程序員的懶惰,所以他們但願他們的測試能夠運行得飛快。他們不想爲測試結果等過久。這也就是爲何許多團隊使用某些更快的內存數據庫(例如HSQLDB)的緣由。因爲那些內存數據庫僅存儲在RAM(Random Access Memory,隨機存取存儲器)內存中,並且在操做時不接觸任何硬盤驅動器,所以它們的運行速度要快得多。
還有一條經常被人遺忘的規則:
測試應該使用與生產環境相同的數據庫引擎。
許多程序員會使用某個其餘引擎。常見的解釋很簡單:「咱們的數據庫太慢了,咱們應該使用某個內存數據庫引擎。」。這並非個好主意。這樣你測試的是該其餘引擎,而非你的生產環境中的那個,因此實際上你並沒對你的應用程序進行測試。
我曾經遇到過這個問題。咱們必須在成功鏈接數據庫後經過設置session變量來優化查詢。那個應用程序在生產環境中只使用Oracle數據庫。當設置此變量之後,測試失敗了。並且是全部的測試都失敗了。原來是我不能在HSQLDB內存數據庫中設置此變量,由於它根本不存在。所以,我必須編寫一段糟糕的代碼:在鏈接後,檢查數據庫引擎,並由此決定是否設置此變量。
即便你沒有任何與混合引擎有關的問題,請記住,當你測試其餘非生產環境的數據庫引擎時,你偏偏根本沒對你的應用程序進行測試。
測試有一個通用的、明肯定義的流程。 它很是簡單:
若嘗試背道而馳,則你會被它所傷。
你察覺在測試以後是沒有整理(tidying)的麼?
這點很是重要:必須在測試前準備環境,而非測試以後。你沒法確保測試將可以清理一切。應用程序可能由於某個錯誤、網絡鏈接失敗而退出,或者應用程序可能崩潰(例如,因爲內存不足異常)。該測試如何終止並不重要,真正重要的是該測試運行在爲每一個測試所準備的相同環境中。
我曾犯過這個錯誤:有大量的集成測試,它們會在每次測試後清理全部更改。許多程序員正使用調試器來運行這些測試,而且當發現bug後會在中間中止測試。任何在該測試以後運行的測試會獲得不可預知的且隨機的結果。由於它正運行在已被前一測試所改變的環境中,並且沒有爲其清理整個環境。
在前面的部分中我提到許多有關準備數據庫的內容。我還想補充一點。準備數據庫是不夠的。當你經過清理某些表、加載配件等等準備好數據庫時……還剩下一件事情要作。
在準備完畢後,要檢查數據庫狀態。
你真正須要確保的是你已將一切準備穩當。當出現因爲bug所致使的某些數據留下來且未能清理時,這些工做就能夠節省你的時間。
這應該是測試前數據庫準備的一部分。
每一個應用程序都須要某種形式的安裝過程。而對於你部署數據庫而言,永遠是第一次。
程序員每每會經過手工執行某些臨時的數據定義語言(DDL,data definition language)查詢來改變數據庫。他們稍後並無把那些語句寫下來,或是忘記了所作的改動。他們沒有更新安裝腳本。大多數團隊不使用有版本控制的腳本(例如,Ruby on Rails中的Migrations、或者是Java世界中的Liquibase)。
對測試而言最好的方式是,在運行測試套件前從新建立數據庫。你沒必要在每一個測試開始前都那麼作。只在運行全部測試前運行一次就好了。
是的,寧肯事先謹慎有餘,不要過後追悔莫及。
外鍵是提供數據庫一致性的基本途徑之一。在良好的關係數據庫schema中,你應該擁有各類鍵。若是你沒有的話,那麼這可能代表你有一個真正的大問題。然而,這取決於數據模型,可是一般缺少外鍵是種很是糟糕設計的味道。
測試外鍵很簡單。只需在事先沒有在引用表中添加適當的行的狀況下,爲某個表添加一些行。你應該獲得一個錯誤。而後,你應該從引用表中刪除行,你可能獲得錯誤,或沒有錯誤(這取決於該鍵的定義)。不管如何,你都應該檢查一下預期行爲。
在良好的數據庫設計中,你應該定義一些合理的默認值。一般這些默認值多是空(null)。即使這些空也應該進行測試。你不能假設,只有你的應用程序將改變此數據庫中的數據。\ 兩個問題:
在數據庫中還有更多約束,不只只有主鍵和外鍵約束。你可能擁有一些惟一的(unique)或不爲空的列。你可能約束某列只有不多的值集。你可能想確保價格永遠不會低於0。
良好的數據庫schema應擁有許多約束。你也應該測試它們。若是你但願你的價格列只能擁有正值,當你嘗試向其中插入-1美圓時會發生些什麼?爲何不測試一下呢?
你不能假設只有你的通過良好測試的應用程序將使用那些數據,並且這些檢查是爲你防護這些bug的最後一道防線。爲何不測試它是否正常工做?
一般,數據庫測試會更改數據庫。你可能同時運行多個測試,可是你必須確保那些測試彼此之間沒有影響。你必須確保,若是某個測試將一些內容寫入數據庫,而另外一測試將不會讀到。
一般,很容易搞得一塌糊塗,所以我小小的忠告是:避免在同一時間運行多個測試。這也意味着,你不該該在多臺機器運行相同的測試套件。
當你有許多想運行測試的開發者時,他們每一個人應該擁有可用於編寫測試的單獨的數據庫。若是你擁有某種形式的只讀數據庫,不要緊,多臺機器能夠在同一時間使用這個數據庫。可是若是你容許全部程序員使用同一數據庫進行測試的情形出現,那麼你可能真的會獲得不可預知的測試結果。
當程序員在某個測試中發現一個錯誤時會怎麼作?那麼,優秀的程序員會盡可能修正錯誤。若是該測試失敗僅僅是由於另外一程序員在同一個數據庫上運行他的測試所致使的話,那麼修正此類錯誤只是在浪費程序員的時間。
優秀的程序員是懶惰的。若是你命令優秀的程序員每次都重複一樣的任務,他們會愈來愈沮喪。優秀的程序員會自動化可重複的事情。
在每一個項目中,你必須在測試環境中部署某些東西。作這些會花去多少時間?你真的想爲了從新部署應用程序和加載數據庫一直浪費你的程序員時間麼?
這就是爲何每一個項目都應該有個大紅按鈕的緣由。某位程序員能夠按下此按鈕,而後衝杯咖啡,回去工做,而且幾分鐘後得知的大紅色按鈕完成的工做,一切準備就緒。
大紅按鈕真的會爲你節省不少時間。你會說自動化全部工做實在太緩慢。然而,事實並不是如此。偏偏相反,就像說測試驅動開發(TDD,Test-Driven Development)很慢同樣。在最初的時候比較慢,但隨着項目變得更加複雜,因爲存在測試或按鈕,會爲你節省更多的時間。各類各樣的大紅按鈕——你能夠用它們部署應用程序、運行測試,以及相似的後方支援。
有時會使用Jenkins(又名Hudson)來作這些。是的,對於此類大紅按鈕而言這是一款偉大的軟件。惟一的事情是,每位程序員應該有其本身的一組工做以便在其本身的環境中部署全部的內容,在其本身的環境中他(或她,固然)能夠自由發揮,而不會影響他人,一樣也不會受到他人的影響。
有許多數據庫測試工具。爲了測試整個schema,你能夠編寫簡單的集成測試。對於PostgreSQL有pgTAP,使用TAP插件它能夠與Jenkins集成到一塊兒,所以Jenkins能夠擁有一項用於測試數據庫(包括生產數據庫)是否正常的工做。