如果您像我一樣,則可能已經花費了很多時間在查詢分析器中開發代碼。在您對代碼感到滿意之後,可以立即對開發服務器上的測試數據庫運行一個或兩個專設 測試。如果看起來沒有什麼問題,您便可以將代碼投入生產。如果這是一段關鍵代碼,或者該代碼較為復雜,則您可能會執行更多的檢查,以避免後驗剖析。甚至在這種情況下,您也可能屏息以待。www.chinai tp 采集 ow er.comd1nHRZf
這就是我在大部分職業生涯中所采用的編碼方式。哦,有時我會存儲測試查詢以供將來使用,這通常是因為總裁/CEO/CIO/部門經理習慣於大約每周就改變一下他或她的要求。但是,除此以外,我不會再做什麼。我通常在查詢分析器或它的 Oracle/Access/FoxPro 等效工具外部用專設 查詢進行測試。更高強度的測試需要使用查詢分析器調試器。在絕望的情形下,需要使用 PRINT 語句。www.chinai tp 采集 ow er.comd1nHRZf
目前存在 一種更好的方式。www.chinai tp 采集 ow er.comd1nHRZf
超越專設 測試www.chinai tp 采集 ow er.comd1nHRZf
當我的 SIL 部門采用極限編程 (XP) 時,我們還采用了該方法論的單元測試部分,而它們兩者都使我成為更出色的開發人員。但是,即使您不在 XP 環境中工作,您仍然可以從 XP 風格的單元測試中獲益。 www.chinai tp 采集 ow er.comd1nHRZf
單元測試不同於接受測試。單元測試用於測試較小的代碼塊(例如,存儲過程),而接受測試更多地涉及到用戶是否可以接受 UI。以下是我發現的單元測試的五個優點:www.chinai tp 采集 ow er.comd1nHRZf
•
它們能夠找出應該承擔責任的當事人。您是否收到過電子郵件,告訴您應該修復程序錯誤,而這實際上是其他某個人所作更改的副作用?好,如果您具有一些零散的測試查詢,請將它們包裝到可以定期運行(或許是在晚上)的存儲過程中。請確保在單元測試失敗時能夠生成電子郵件。 www.chinai tp 采集 ow er.comd1nHRZf
•
;生成庫不需要花費很長時間。每個存儲過程和每個存儲函數都應當具有為它編寫的測試,而觸發器也應該如此。如果這聽起來有些苛刻,那麼請想一想,能夠在問題到達生產服務器之前捕捉到它,從而拯救您自己,將會是一種多麼好的感覺。如果您具有大量舊式代碼,那麼為每個單元編寫測試可能需要多年的工作,並且您也不能僅僅為了編寫測試而停止新的開發工作。但是,您可以為每段新代碼編寫測試,也可以為您修改的每個過程編寫測試。用不了多長時間,您就會為關鍵的舊式代碼和新代碼編寫眾多的測試。 www.chinai tp 采集 ow er.comd1nHRZf
•
輕松創建准確的代碼文檔。每個過程或函數都應當用不同的參數組合調用。這不僅能夠確保代碼按預期方式工作,而且還提供了有關您的工作的最新而准確的文檔。另外一個編碼員只需查看您的測試,就可以了解對您的過程進行調用的示例。誰知道呢?某一天,這另一個編碼員可能就是您自己。 www.chinai tp 采集 ow er.comd1nHRZf
•
它們迫使您預先進行一點兒思考和計劃。您應當在編寫實際的過程或函數之前編寫自己的單元測試。“什麼?”您說,“我抗議!我們如何為尚未進行編碼的東西編寫測試?” www.chinai tp 采集 ow er.comd1nHRZf
•
有一個很老的笑話,它講的是:有一個經理說:“我將弄清楚他們需要什麼。其余的人開始編碼。”那麼,編碼員在知道他們需要編寫什麼之前是無法開始工作的,不是嗎?當您首先編寫測試時,您將被迫考慮在開始編寫該過程之前,您希望該過程完成什麼工作。 www.chinai tp 采集 ow er.comd1nHRZf
•
它們確實可以節省您的時間。開發人員經常抱怨,編寫測試需要花費比編寫實際過程更多的時間。有時的確如此。但是請考慮以下情況:我最近接受了一項任務,即,修改我曾經遇到過的最難的存儲過程之一。它是舊式代碼,但是我仍然首先編寫了測試。它花費了我幾天的時間才完成,部分原因在於對該過程所施加的要求。實踐證明,出於我剛才列出的所有原因,該測試非常重要,並且當我必須重新編寫該過程以改善性能時,它變得彌足珍貴。 www.chinai tp 采集 ow er.comd1nHRZf
•
單元測試顯示重新編寫的過程中存在大量錯誤,而我能夠很快地找到每個錯誤的根源,所花費的時間只占不使用單元測試時的幾分之一。然後,當我認為已經完成該任務時,模糊測試失敗了。主循環中的變量之一存在缺陷。如果代碼以這種狀態發布到生產環境中,那麼這將是一個難以捕獲的程序錯誤。最終,我以比采用其他方式更快的速度完成了這項任務。 www.chinai tp 采集 ow er.comd1nHRZf
如何編寫 T-SQL 單元測試www.chinai tp 采集 ow er.comd1nHRZf
在我告訴您有關 T-SQL 測試框架的內容之前,首先需要提醒您注意兩個非常基本的原則:www.chinai tp 采集 ow er.comd1nHRZf
•
第一,您需要一個具有良好測試數據的數據庫。我用“良好數據”表示來自現實世界的真實數據。無論您是一個多麼優秀的程序員,都無法充分地為應用程序仿造數據。即使要替換的舊式系統由紙張組成,也要找一位數據錄入員來在某些表中輸入數據。完成獲得真實數據所需的工作。[盡管如此,仍然存在測試數據生成器。請參見本期中我的提示“生成測試數據”— 編者]www.chinai tp 采集 ow er.comd1nHRZf
•
第二,不應當針對生產數據庫進行開發。您應當具有一個開發或測試數據庫,以便滿足您自己的需要。過去,當我在 Oracle 進行開發時,我曾經花費了一周的時間將開發數據庫放在一個陳舊的服務器上。SQL Server 開發人員沒有這樣的借口。 www.chinai tp 采集 ow er.comd1nHRZf
在為開發數據庫配備良好的數據以後,您需要某種框架以便運行測試。您可以編寫自己的框架,但是為什麼要這麼做呢?已經有一個可用的框架了。www.chinai tp 采集 ow er.comd1nHRZf
TSQLUnit 簡介www.chinai tp 采集 ow er.comd1nHRZf
TSQLUnit 是 T-SQL 的一個開放源碼單元測試框架,它由 Henrik Ekelund 編寫,並且可以從 http://sourceforge.Net/projects/tsqlunit 獲得。以下是一個有關我如何使用它的示例。www.chinai tp 采集 ow er.comd1nHRZf
我的 TSQLUnit 測試采用了類似的三部分模式:1) 單元測試設置,2) 執行目標過程,和 3) 檢查結果。www.chinai tp 采集 ow er.comd1nHRZf
在單元測試設置過程中,我經常進行檢查,以確保沒有人趁我不注意時破壞了我的數據:www.chinai tp 采集 ow er.comd1nHRZf
DECLARE @nId INT, @nNewId INT —- @nNewId is for later
SELECT @nId = [ID] FROM MyTable
WHERE MyFIEld = ’whatever’
IF @nId IS NULL -- or @@ROWCOUNT = 0
EXEC tsu_failure ’The data has changed.
’whatever’ couldn’t be found’
IF 塊用於檢查預期的記錄。如果找不到該記錄,則測試會失敗,並且會生成錯誤信息。測試框架移動至下一個單元測試。您不需要在失敗消息字符串中使用該單元測試的名稱,因為當測試失敗時,TSQLUnit 將為您命名它。www.chinai tp 采集 ow er.comd1nHRZf
現在,我調用將要編寫的存儲過程:www.chinai tp 采集 ow e
您正在看的MySQL教程是:查詢分析器中開發代碼測試檢查。r.comd1nHRZf
EXEC CreateMyTableNewRec @nId, @nNewId OUTPUT
正如您看到的那樣,我已經確定了需要來自這一新過程的輸出參數。在檢查結果的過程中,我確保輸出參數確實填充了某些內容:www.chinai tp 采集 ow er.comd1nHRZf
IF @nNewId IS NULL
EXEC tsu_failure
’A new record was not created for table MyTable.’
我可以進一步檢查該值,以查看新記錄是否是按照我希望的方式創建的。www.chinai tp 采集 ow er.comd1nHRZf
每個 TSQLUnit 測試本身都是一個存儲過程。清單 1 顯示了在將上述所有代碼段放在一起時所具有的樣子:www.chinai tp 采集 ow er.comd1nHRZf
清單 1. T-SQL 的完整單元測試。www.chinai tp 采集 ow er.comd1nHRZf
CREATE PROCEDURE ut_MyTable_NewRec
AS
--== Setup ==--
DECLARE @nID INT, @nNewId INT
SELECT @nId = ID FROM MyTable
WHERE MyFIEld = ’whatever’
IF @nId IS NULL -- or @@ROWCOUNT = 0
EXEC tsu_failure ’The data has changed.
46;Whatever’ couldn’t be found’
--== Execute ==--
EXEC CreateMyTableNewRec @nId, @nNewId OUTPUT
--== Ch