原文信息
數據庫單元測試工具-SQLUnit
前言
正如之前所說,我已經改變了原有寫代碼方式。不是立馬就寫出測試用例的“測試驅動開發”(我知道這種開發理論很難站住腳)。但當我預計到這種編程方式必定存在問題時,這已經是一個好的開始了。在一條帶有自動序列號的主鍵的 INSERT 語句下,我是否應該轉換程序中異常為自己的系統的錯誤代碼?或者僅僅讓 RA-00001 傳播開?我采取的辦法是,僅僅彈出 Oracle 自身提示的錯誤碼,沒有必要自己為出現錯誤再重新創建一套錯誤碼機制。如果你在某種情況下得到一個錯誤碼,已經很嚴肅地告訴了用戶當前有些事情已經出錯了,並且全世界的人都應該知道系統出錯了。
說說異常吧,使用它們,拋出它們,不要試圖捕捉它們(大部分情況下)並且來做一些其他的事情。將異常記錄下來然後把它們拋向上一層。我想:讓每一個開發者都知道當前狀況已經開始變壞,這是一件很好的事情。我曾花費太多的時間來通過調試代碼來嘗試找出系統的錯誤,但大部分的異常已經被捕捉了而且進程繼續往下跑,導致我浪費了很多時間。至少在某些情況下,我幸運地通過記錄錯誤的日志來解決的問題。
什麼是 SQLUnit
SQLUnit 是一個回歸測試數據庫存儲過程的單元測試工具。一個 SQLUnit 測試用例應該用 XML 文件來書寫。SQLUnit工具 是用 Java 實現的,使用 JUnit 單元測試礦建轉換 XML 測試信息到 JDBC 數據庫連接,比較從數據庫中得到的結果和測試用例中的預期結果。不幸的是,它僅僅只持續了三年的開發過程,但我想說的是:這是一個相當好的測試模型。支持存儲過程、函數、游標以及用戶自定義類型(盡管我還沒有嘗試過)。當前最新版本是 5.0.我一直在用的是 1.3 版本。一個大學同學(現在在Oracle 公司工作),在我剛開始使用 SQLUnit 測試工具的時候,他甚至為SQLUnit 中 Oracle 數據庫的測試部分貢獻了代碼。
我通過它來為 CABEZE 構建數據庫,這是我第一次為自己的事業做的項目,盡管最終沒有成功,但也很好,因為我只是在一些零零碎碎、不太重要的代碼上建立起的項目,所以我可以通過 SQLUnit 來構建整個測試數據(不是測試實際上的產品數據...盡管也沒有實際的設備),建立起數據庫(創建測試數據),運行測試用例並且最後在銷毀階段回滾到原始狀態(空的)。不幸的是,我工作中構建的那個系統不是空的並且是以實際產品進行測試的,或者半生產(清洗) 數據是僅僅是一種可行的替代品.
回到現在我的狀態。我正嘗試重新認識自己,在這個工具的幫助下為多種多樣的存儲過程書寫測試用例。我跟進一個報出錯誤的測試用例中,因為我們所有的信用卡號都是雜亂的。每一次在不正確的卡號下失敗。攀附之物。
為什麼不創建一個例程,一個可以生產“實際”信用卡號,更精確的說是:一串已經檢測了數字的合適長度的數列?所以信用卡使用 Luhn 公式用於阻止這些明顯的調換錯誤。
這 Luhn 算法將檢測任何奇數的錯誤,大部分的調換臨近的數組。它將不是,然而,檢測 調換位置兩數字序列 09-90(或者副動詞)。它將檢測到 在 10 個可能重復數字中檢測出7個(它不能檢測出 22 ? 55, 33 ? 66 or 44 ? 77)。
在 CABEZE 下,我已經重寫了我自己的 PL/SQL
卡號生成器(並且通過驗證),但現在我不把它公布出來,而且我好像已經丟失了那部分代碼。所以,我嘗試重寫一遍。
從我開始使用 SQLUnit 後,我體驗到這個工具演示了一個多麼強大的功能呀!它擁有一些復雜(對於我來說)的公式,所以在寫這些測試用例的時候能夠幫我計算出結果。這是我創建堅持測試基於 Luhn 公式的數字序列。
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 CREATE OR REPLACE FUNCTION create_check_digit( p_card_number IN NUMBER ) RETURN NUMBER IS TYPE t_digits IS TABLE OF INTEGER; l_table T_DIGITS := T_DIGITS(); l_count INTEGER := 0; l_num INTEGER; l_digit INTEGER; l_odd INTEGER := 0; l_even INTEGER := 0; l_sum INTEGER := 0; l_check_digit INTEGER; BEGIN IF p_card_number IS NULL THEN raise_application_error( -20001, 'you must provide a card number' ); END IF; FOR i IN REVERSE 1..LENGTH( p_card_number ) LOOP l_count := l_count + 1; l_table.EXTEND(1); l_table( l_count ) := SUBSTR( p_card_number, i, 1 ); END LOOP; FOR i IN 1..l_table.COUNT LOOP l_digit := l_table(i); IF MOD( i, 2 ) > 0 THEN l_num := l_digit * 2; IF l_num > 9 THEN FOR i IN 1..LENGTH( l_num ) LOOP l_odd := l_odd + SUBSTR( l_num, i, 1 ); END LOOP; ELSE l_odd := l_num; END IF; p( 'odd: ' || l_odd ); ELSE l_even := l_digit; END IF; l_sum := l_sum + l_odd + l_even; p( 'l_sum: ' || l_sum ); l_odd := 0; l_even := 0; END LOOP; l_check_digit := ABS( ( CEIL( MOD( l_sum / 10, 10 ) ) * 10 ) - l_sum ); p( 'check digit: ' || l_check_digit ); p( 'l_sum: ' || l_sum ); p( p_card_number || l_check_digit ); RETURN l_check_digit; END create_check_digit;(譯注:一大堆的 Oracle 存儲過程函數)
我沒有意識到這可以做到更簡單些,特別是一些常規的表達式。這僅僅是我第一次嘗試...所以不要讓我難堪...如果有更好的解決辦法,請留言。謝謝。 ;)
這是我最終測試的輸出結果:
?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 [sqlunit] *** Running SQLUnit file: p_cc.xml [sqlunit] Getting connection(DEFAULT) [sqlunit] Setting up test... [sqlunit] Running test[1]: PASSING NULL (125ms) [sqlunit] Running test[2]: VALID CARD NUMBER (4992739871) (15ms) [sqlunit] Running test[3]: VALID CARD NUMBER (4012888888881881) (16ms) [sqlunit] Running test[4]: VALID CARD NUMBER (4111111111111111) (0ms) [sqlunit] Running test[5]: VALID CARD NUMBER (4222222222222) (15ms) [sqlunit] Running test[6]: RANDOM (1) NUMBER (5) (0ms) [sqlunit] Running test[7]: RANDOM (2) NUMBER (55) (0ms) [