99999久久久久久亚洲,欧美人与禽猛交狂配,高清日韩av在线影院,一个人在线高清免费观看,啦啦啦在线视频免费观看www

熱線電話:13121318867

登錄
首頁精彩閱讀永遠不要忘記數(shù)據(jù)庫測試 數(shù)據(jù)庫測試的重要性和測試要點說明
永遠不要忘記數(shù)據(jù)庫測試 數(shù)據(jù)庫測試的重要性和測試要點說明
2014-12-21
收藏

永遠不要忘記數(shù)據(jù)庫測試 數(shù)據(jù)庫測試的重要性和測試要點說明


譯者導(dǎo)讀:本文分為三部分,第一部分是第1節(jié),即說明“對數(shù)據(jù)庫測試的根本誤解”;第二部分從第2節(jié)至倒數(shù)第4節(jié),詳述“數(shù)據(jù)庫測試測什么”的問題;第三部分是最后3節(jié),引出“數(shù)據(jù)庫測試怎么測”的問題,提出自動部署、自動化測試、持續(xù)集成的思路及工具。另,副標題是俺自行添上去的,以明示本文意圖。

對數(shù)據(jù)庫測試的根本誤解

有許多關(guān)于測試驅(qū)動開發(fā)(Test-Driven Development,縮寫為TDD)的書籍。那些書通常關(guān)注的是將測試應(yīng)用于工作單元(units of work)。對于工作單元的理解有許多種不同的方式,通常它表示一個類(class)。正如那些書中所言:編寫許多測試,以使那些測試都能通過的方式創(chuàng)建代碼。應(yīng)模擬所有的外部資源,以便你可以只測試這個單元。

這很酷,但不幸的是所有的測試在此刻停止了。因為通常會有些沒被測到的查詢(手寫的或者是由某些ORM工具生成的)。有些程序員使用集成測試來測試那些查詢——連接到一個真實的數(shù)據(jù)庫并執(zhí)行真實的查詢來進行測試。這種做法通常意味著在測試快樂路徑(happy path)——我已經(jīng)有了ORM工具,所以它會搞定每件事,我無須費心。

數(shù)據(jù)庫通常是一家公司最有價值的資產(chǎn)。應(yīng)用程序可以一遍一遍重寫。舊的應(yīng)用程序扔出去,新的應(yīng)用程序裝進來。但是更換應(yīng)用程序時沒人會丟棄滿載數(shù)據(jù)的數(shù)據(jù)庫。而是將數(shù)據(jù)庫小心翼翼地遷移過去。位于多個系統(tǒng)中的許多不同的應(yīng)用程序會在同一時刻使用同一數(shù)據(jù)庫。這就是為什么擁有充滿約束的良好數(shù)據(jù)庫模型是如此重要、以及為什么應(yīng)謹慎對待數(shù)據(jù)庫的原因。你真的不想破壞數(shù)據(jù)一致性(consistency),因為這會使你的公司付出高昂的代價。

本文是關(guān)于經(jīng)常被遺忘的數(shù)據(jù)庫測試的。使用真實數(shù)據(jù)進行集成測試。實際上,它與你所使用的數(shù)據(jù)庫引擎的類型無關(guān)緊要。你可以使用PostgreSQL、MySQL、Oracle,或者甚至使用那些有趣的noSQL數(shù)據(jù)庫,例如MongoDB。以下規(guī)則可適用于各種數(shù)據(jù)庫和各類應(yīng)用程序。也許不是全部,例如noSQL數(shù)據(jù)庫就無法強制實施數(shù)據(jù)完整性(integrity)。

你的應(yīng)用程序通常是由許多不同的部件組成的。其中有一些<將任何你喜歡的語言放在這里>代碼,一些配置文件,一些SQL查詢,一些外部系統(tǒng)。測試一個應(yīng)用程序意味著分別測試每個部件(因為只有這樣才更容易找出bug)、以及測試所有部件是如何協(xié)作的。數(shù)據(jù)庫就是這些部件的其中之一,而且你應(yīng)該徹底測試它。

不測試數(shù)據(jù)庫

這是首要的、最可怕的錯誤。根本不測試數(shù)據(jù)庫。你編寫了一些使用數(shù)據(jù)庫的代碼。你甚至使用一些模擬數(shù)據(jù)庫連接為這些類創(chuàng)建了單元測試。

集成測試怎么樣?集成測試應(yīng)在生產(chǎn)環(huán)境下對應(yīng)用程序進行測試。集成測試背后的唯一想法是,確保應(yīng)用程序部署到生產(chǎn)環(huán)境后可以正常工作。如果你不在生產(chǎn)數(shù)據(jù)庫上測試應(yīng)用程序,那么你實際上不并不知道應(yīng)用程序能否工作。你的模擬連接讓你可以發(fā)送尚未檢查以及沒有檢查的任何查詢。模擬連接只返回你所需的數(shù)據(jù)。

不創(chuàng)建集成測試意味著你實際上沒有測試你的應(yīng)用程序。

不測試數(shù)據(jù)庫Schema(模式/架構(gòu))

我所觀察過的大多數(shù)團隊擁有某種形式的集成測試。通常進行快樂路徑測試:有某個ORM工具,我們持久化對象,ORM工具會完成那些工作,真是太酷了,我無須費心。

我從未見過一支對數(shù)據(jù)庫schema(模式/架構(gòu))進行測試的團隊。想象一下,由于某些針對產(chǎn)品的查詢很慢,因此你必須在該數(shù)據(jù)庫中創(chuàng)建某個索引。當下次在新的客戶環(huán)境中部署此應(yīng)用程序時,你希望擁有該索引并確認該索引真的就在那里。為什么不編寫一個簡單的測試來檢查該索引的存在呢?

除了索引,還有許多要測試的內(nèi)容:

  • 主鍵(primary keys)
  • 外鍵(foreign keys)
  • 一些檢查——以確?!皃rice”(價格)列不會有負值
  • 某些列的唯一性(uniqueness)——你實際上不想擁有兩個具有相同登錄名的用戶。

不在生產(chǎn)環(huán)境下測試

當你開發(fā)某個應(yīng)用程序時,你可以從種類繁多的數(shù)據(jù)庫中進行選擇。通常你會從中選擇那個最好的、那個被團隊所熟知的、或者是由管理層所選定的(有時使用一些奇怪的理由)。有時同一應(yīng)用程序的多個部署會在同一時間使用不同的數(shù)據(jù)庫引擎。有時應(yīng)用程序會為了能使用不同的數(shù)據(jù)庫引擎進行準備,因此購買此應(yīng)用程序的客戶就可以選擇他想要的數(shù)據(jù)庫。

數(shù)據(jù)庫引擎的選擇真的與進行產(chǎn)品測試無關(guān)。

由于程序員的懶惰,因此他們希望他們的測試可以運行得飛快。他們不想為測試結(jié)果等太久。這也就是為什么許多團隊使用某些更快的內(nèi)存數(shù)據(jù)庫(例如HSQLDB)的原因。由于那些內(nèi)存數(shù)據(jù)庫僅存儲在RAM(Random Access Memory,隨機存取存儲器)內(nèi)存中,而且在操作時不接觸任何硬盤驅(qū)動器,因此它們的運行速度要快得多。

還有一條常常被人遺忘的規(guī)則:

測試應(yīng)該使用與生產(chǎn)環(huán)境相同的數(shù)據(jù)庫引擎。

許多程序員會使用某個其他引擎。常見的解釋很簡單:“我們的數(shù)據(jù)庫太慢了,我們應(yīng)該使用某個內(nèi)存數(shù)據(jù)庫引擎。”。這并不是個好主意。這樣你測試的是該其他引擎,而非你的生產(chǎn)環(huán)境中的那個,所以實際上你并沒對你的應(yīng)用程序進行測試。

我曾經(jīng)遇到過這個問題。我們必須在成功連接數(shù)據(jù)庫后通過設(shè)置session變量來優(yōu)化查詢。那個應(yīng)用程序在生產(chǎn)環(huán)境中只使用Oracle數(shù)據(jù)庫。當設(shè)置此變量以后,測試失敗了。而且是所有的測試都失敗了。原來是我不能在HSQLDB內(nèi)存數(shù)據(jù)庫中設(shè)置此變量,因為它根本不存在。因此,我必須編寫一段糟糕的代碼:在連接后,檢查數(shù)據(jù)庫引擎,并由此決定是否設(shè)置此變量。

即使你沒有任何與混合引擎有關(guān)的問題,請記住,當你測試其他非生產(chǎn)環(huán)境的數(shù)據(jù)庫引擎時,你恰恰根本沒對你的應(yīng)用程序進行測試。

不準備好數(shù)據(jù)庫就進行測試

測試有一個通用的、明確定義的流程。 它非常簡單:

  • 準備環(huán)境
  • 運行一個測試
  • 檢查測試結(jié)果
  • 返回至第一點

若嘗試背道而馳,則你會被它所傷。

你察覺在測試之后是沒有整理(tidying)的么?

這點非常重要:必須在測試前準備環(huán)境,而非測試之后。你無法確保測試將能夠清理一切。應(yīng)用程序可能因為某個錯誤、網(wǎng)絡(luò)連接失敗而退出,或者應(yīng)用程序可能崩潰(例如,由于內(nèi)存不足異常)。該測試如何終止并不重要,真正重要的是該測試運行在為每個測試所準備的相同環(huán)境中。

我曾犯過這個錯誤:有大量的集成測試,它們會在每次測試后清理所有更改。許多程序員正使用調(diào)試器來運行這些測試,并且當發(fā)現(xiàn)bug后會在中間停止測試。任何在該測試之后運行的測試會得到不可預(yù)知的且隨機的結(jié)果。因為它正運行在已被前一測試所改變的環(huán)境中,而且沒有為其清理整個環(huán)境。

準備了數(shù)據(jù)庫卻不對其檢查就進行測試

在前面的部分中我提到許多有關(guān)準備數(shù)據(jù)庫的內(nèi)容。我還想補充一點。準備數(shù)據(jù)庫是不夠的。當你通過清理某些表、加載配件等等準備好數(shù)據(jù)庫時……還剩下一件事情要做。

在準備完畢后,要檢查數(shù)據(jù)庫狀態(tài)。

你真正需要確保的是你已將一切準備妥當。當出現(xiàn)由于bug所導(dǎo)致的某些數(shù)據(jù)留下來且未能清理時,這些工作就可以節(jié)省你的時間。

這應(yīng)該是測試前數(shù)據(jù)庫準備的一部分。

不測試創(chuàng)建腳本

每個應(yīng)用程序都需要某種形式的安裝過程。而對于你部署數(shù)據(jù)庫而言,永遠是第一次。

程序員往往會通過手工執(zhí)行某些臨時的數(shù)據(jù)定義語言(DDL,data definition language)查詢來改變數(shù)據(jù)庫。他們稍后并沒有把那些語句寫下來,或是忘記了所做的改動。他們沒有更新安裝腳本。大多數(shù)團隊不使用有版本控制的腳本(例如,Ruby on Rails中的Migrations、或者是Java世界中的Liquibase)。

對測試而言最好的方式是,在運行測試套件前重新創(chuàng)建數(shù)據(jù)庫。你不必在每個測試開始前都那么做。只在運行所有測試前運行一次就行了。

是的,寧可事先謹慎有余,不要事后追悔莫及。

不測試外鍵

外鍵是提供數(shù)據(jù)庫一致性的基本途徑之一。在良好的關(guān)系數(shù)據(jù)庫schema中,你應(yīng)該擁有各種鍵。如果你沒有的話,那么這可能表明你有一個真正的大問題。然而,這取決于數(shù)據(jù)模型,但是通常缺乏外鍵是種非常糟糕設(shè)計的味道。

測試外鍵很簡單。只需在事先沒有在引用表中添加適當?shù)男械那闆r下,為某個表添加一些行。你應(yīng)該得到一個錯誤。然后,你應(yīng)該從引用表中刪除行,你可能得到錯誤,或沒有錯誤(這取決于該鍵的定義)。無論如何,你都應(yīng)該檢查一下預(yù)期行為。

不測試默認值

在良好的數(shù)據(jù)庫設(shè)計中,你應(yīng)該定義一些合理的默認值。通常這些默認值可能是空(null)。即便這些空也應(yīng)該進行測試。你不能假設(shè),只有你的應(yīng)用程序?qū)⒏淖兇藬?shù)據(jù)庫中的數(shù)據(jù)。\ 兩個問題:

  • 如果某人想創(chuàng)建一個快速修復(fù)并使用臨時SQL查詢更新某些數(shù)據(jù),該怎么辦?
  • 如果有一天某人啟動另一應(yīng)用程序,它可以改變這些數(shù)據(jù),而且新的應(yīng)用程序?qū)⒉皇褂媚愕腛RM映射或你的DAO(數(shù)據(jù)訪問對象)類,又該怎么辦?
  • 如果你擁有愚蠢的默認值、或是錯誤的默認值,那么你可能會破會數(shù)據(jù),而且那可能比一個簡單的應(yīng)用程序bug更糟糕。

不測試約束

在數(shù)據(jù)庫中還有更多約束,不僅只有主鍵和外鍵約束。你可能擁有一些唯一的(unique)或不為空的列。你可能約束某列只有很少的值集。你可能想確保價格永遠不會低于0。

良好的數(shù)據(jù)庫schema應(yīng)擁有許多約束。你也應(yīng)該測試它們。如果你希望你的價格列只能擁有正值,當你嘗試向其中插入-1美元時會發(fā)生些什么?為什么不測試一下呢?

你不能假設(shè)只有你的經(jīng)過良好測試的應(yīng)用程序?qū)⑹褂媚切?shù)據(jù),而且這些檢查是為你防御這些bug的最后一道防線。為什么不測試它是否正常工作?

多個測試可以更改同一數(shù)據(jù)庫

通常,數(shù)據(jù)庫測試會更改數(shù)據(jù)庫。你可能同時運行多個測試,但是你必須確保那些測試彼此之間沒有影響。你必須確保,如果某個測試將一些內(nèi)容寫入數(shù)據(jù)庫,而另一測試將不會讀到。

通常,很容易搞得一塌糊涂,因此我小小的忠告是:避免在同一時間運行多個測試。這也意味著,你不應(yīng)該在多臺機器運行相同的測試套件。

當你有許多想運行測試的開發(fā)者時,他們每個人應(yīng)該擁有可用于編寫測試的單獨的數(shù)據(jù)庫。如果你擁有某種形式的只讀數(shù)據(jù)庫,沒關(guān)系,多臺機器可以在同一時間使用這個數(shù)據(jù)庫。但是如果你允許所有程序員使用同一數(shù)據(jù)庫進行測試的情形出現(xiàn),那么你可能真的會得到不可預(yù)知的測試結(jié)果。

當程序員在某個測試中發(fā)現(xiàn)一個錯誤時會怎么做?那么,優(yōu)秀的程序員會盡量修正錯誤。如果該測試失敗僅僅是因為另一程序員在同一個數(shù)據(jù)庫上運行他的測試所導(dǎo)致的話,那么修正此類錯誤只是在浪費程序員的時間。

沒有大紅按鈕

優(yōu)秀的程序員是懶惰的。如果你命令優(yōu)秀的程序員每次都重復(fù)同樣的任務(wù),他們會越來越沮喪。優(yōu)秀的程序員會自動化可重復(fù)的事情。

在每個項目中,你必須在測試環(huán)境中部署某些東西。做這些會花去多少時間?你真的想為了重新部署應(yīng)用程序和加載數(shù)據(jù)庫一直浪費你的程序員時間么?

這就是為什么每個項目都應(yīng)該有個大紅按鈕的原因。某位程序員可以按下此按鈕,然后沖杯咖啡,回去工作,并且?guī)追昼姾蟮弥拇蠹t色按鈕完成的工作,一切準備就緒。

大紅按鈕真的會為你節(jié)省很多時間。你會說自動化所有工作實在太緩慢。然而,事實并非如此。恰恰相反,就像說測試驅(qū)動開發(fā)(TDD,Test-Driven Development)很慢一樣。在最初的時候比較慢,但隨著項目變得更加復(fù)雜,由于存在測試或按鈕,會為你節(jié)省更多的時間。各種各樣的大紅按鈕——你可以用它們部署應(yīng)用程序、運行測試,以及類似的后方支援。

有時會使用Jenkins(又名Hudson)來做這些。是的,對于此類大紅按鈕而言這是一款偉大的軟件。唯一的事情是,每位程序員應(yīng)該有其自己的一組工作以便在其自己的環(huán)境中部署所有的內(nèi)容,在其自己的環(huán)境中他(或她,當然)可以自由發(fā)揮,而不會影響他人,同樣也不會受到他人的影響。

工具

有許多數(shù)據(jù)庫測試工具。為了測試整個schema,你可以編寫簡單的集成測試。對于PostgreSQL有pgTAP,使用TAP插件它可以與Jenkins集成到一起,因此Jenkins可以擁有一項用于測試數(shù)據(jù)庫(包括生產(chǎn)數(shù)據(jù)庫)是否正常的工作。

SQL

數(shù)據(jù)分析咨詢請掃描二維碼

若不方便掃碼,搜微信號:CDAshujufenxi

數(shù)據(jù)分析師資訊
更多

OK
客服在線
立即咨詢
客服在線
立即咨詢
') } function initGt() { var handler = function (captchaObj) { captchaObj.appendTo('#captcha'); captchaObj.onReady(function () { $("#wait").hide(); }).onSuccess(function(){ $('.getcheckcode').removeClass('dis'); $('.getcheckcode').trigger('click'); }); window.captchaObj = captchaObj; }; $('#captcha').show(); $.ajax({ url: "/login/gtstart?t=" + (new Date()).getTime(), // 加隨機數(shù)防止緩存 type: "get", dataType: "json", success: function (data) { $('#text').hide(); $('#wait').show(); // 調(diào)用 initGeetest 進行初始化 // 參數(shù)1:配置參數(shù) // 參數(shù)2:回調(diào),回調(diào)的第一個參數(shù)驗證碼對象,之后可以使用它調(diào)用相應(yīng)的接口 initGeetest({ // 以下 4 個配置參數(shù)為必須,不能缺少 gt: data.gt, challenge: data.challenge, offline: !data.success, // 表示用戶后臺檢測極驗服務(wù)器是否宕機 new_captcha: data.new_captcha, // 用于宕機時表示是新驗證碼的宕機 product: "float", // 產(chǎn)品形式,包括:float,popup width: "280px", https: true // 更多配置參數(shù)說明請參見:http://docs.geetest.com/install/client/web-front/ }, handler); } }); } function codeCutdown() { if(_wait == 0){ //倒計時完成 $(".getcheckcode").removeClass('dis').html("重新獲取"); }else{ $(".getcheckcode").addClass('dis').html("重新獲取("+_wait+"s)"); _wait--; setTimeout(function () { codeCutdown(); },1000); } } function inputValidate(ele,telInput) { var oInput = ele; var inputVal = oInput.val(); var oType = ele.attr('data-type'); var oEtag = $('#etag').val(); var oErr = oInput.closest('.form_box').next('.err_txt'); var empTxt = '請輸入'+oInput.attr('placeholder')+'!'; var errTxt = '請輸入正確的'+oInput.attr('placeholder')+'!'; var pattern; if(inputVal==""){ if(!telInput){ errFun(oErr,empTxt); } return false; }else { switch (oType){ case 'login_mobile': pattern = /^1[3456789]\d{9}$/; if(inputVal.length==11) { $.ajax({ url: '/login/checkmobile', type: "post", dataType: "json", data: { mobile: inputVal, etag: oEtag, page_ur: window.location.href, page_referer: document.referrer }, success: function (data) { } }); } break; case 'login_yzm': pattern = /^\d{6}$/; break; } if(oType=='login_mobile'){ } if(!!validateFun(pattern,inputVal)){ errFun(oErr,'') if(telInput){ $('.getcheckcode').removeClass('dis'); } }else { if(!telInput) { errFun(oErr, errTxt); }else { $('.getcheckcode').addClass('dis'); } return false; } } return true; } function errFun(obj,msg) { obj.html(msg); if(msg==''){ $('.login_submit').removeClass('dis'); }else { $('.login_submit').addClass('dis'); } } function validateFun(pat,val) { return pat.test(val); }