為 PL/SQL 構(gòu)建代碼分析實(shí)用工具(三)
2024-07-21 02:34:33
供稿:網(wǎng)友
第二部分:從測試開始
在編寫代碼前建立一個測試計劃,可以節(jié)省您的時間。
在本系列的 第一篇文章 中,我決定構(gòu)建一個可以對代碼進(jìn)行質(zhì)量檢查的實(shí)用工具:非凡是識別 PL/SQL 程序包中具有歧義或者潛在歧義的超載問題。
此外,我還識別數(shù)據(jù)源( ALL_ARGUMENTS 數(shù)據(jù)字典視圖)和代碼( DBMS_DESCRIBE 程序包),以幫助構(gòu)建實(shí)用工具。下一步要做什么呢?
坦率地說,我自然傾向于打開我所喜愛的集成開發(fā)環(huán)境 (IDE) 并開始動指如飛地編寫代碼,投入激動人心的創(chuàng)作之中。我希望在工作時思考并得出結(jié)論,不斷地應(yīng)對挑戰(zhàn),使工作成果運(yùn)行起來,然后對其進(jìn)行微調(diào)。
這種方法有積極的方面(您當(dāng)然不會有過度設(shè)計的風(fēng)險),但也有很多缺點(diǎn)。首先,假如我所使用的構(gòu)建方法是直通式構(gòu)造非線性系統(tǒng) — 常被親切地稱為 HRCLS 或 Hercules (一種 非常 輕型的方法論),我最終不會僅是微調(diào)代碼。絕對不會,在我將范圍縮小到最終目的、目標(biāo)和實(shí)質(zhì)內(nèi)容時,我最終將會以重寫整個程序 — 一次、兩次或三次。但是,盡管可以激動地看到,經(jīng)過自己的努力,實(shí)用工具成形、進(jìn)步并改觀,但這會浪費(fèi)很多的時間。
對于 Codecheck ,我會忍住最初的誘惑。我不會在項(xiàng)目開始的頭 60 秒內(nèi)就編寫代碼,而是做一個簡單的聲明:我保證使我編寫的代碼適用于一個全面的測試計劃。
這與避免 Hercules 綜合癥有什么必然的關(guān)系呢?讓我們來考慮這種保證的含義:
我將制訂一個測試計劃,它非常全面,精心策劃了數(shù)十個(甚至可能有 100 個之多)測試方案,包括參數(shù)的數(shù)量、各種數(shù)據(jù)類型的結(jié)合以及缺省值的有無。
我要設(shè)計、構(gòu)建和運(yùn)行測試,這些測試可確定我的代碼是否滿足測試計劃的要求:換言之,設(shè)計、構(gòu)建和運(yùn)行涵蓋我的全部測試方案的測試。
天哪,這聽起來確實(shí)很有道理,不是嗎?我的意思是,誰 會 一直做所有的這些事情?況且,我們都知道這樣的事實(shí):我們之中很少有人實(shí)際上會花時間,或在手邊有必備的工具去做全面的測試。實(shí)際上我認(rèn)為,世界上至少百分之九十的軟件開發(fā)人員和開發(fā)團(tuán)隊(duì)(包括我自己)遠(yuǎn)遠(yuǎn)不會完全按上述保證去做。
對我來說,在開發(fā)初始時我表明要執(zhí)著地進(jìn)行測試,我發(fā)現(xiàn)這一點(diǎn)促使我實(shí)現(xiàn)了 Codecheck 。
定義范圍,做出假設(shè)
在開發(fā)一項(xiàng)測試策略并構(gòu)造測試之前,我需要確定項(xiàng)目的使用范圍。用戶群通常確定(并經(jīng)常更改)項(xiàng)目的使用范圍。對于 Codecheck 以及我制作的很多其他實(shí)用工具,您,開發(fā)人員,是我的用戶,但我仍然需要確定使用范圍,把您的需求作為我的指導(dǎo)。我的目標(biāo)是產(chǎn)生一個代碼體,能立即幫助開發(fā)人員提高代碼質(zhì)量,但也可以作為一項(xiàng)穩(wěn)定而且可擴(kuò)展的功能,開發(fā)人員可以將其加入代碼中滿足自己的需要。它必須能夠處理現(xiàn)實(shí)世界中足夠的復(fù)雜性,這樣才有用處,但又不可太復(fù)雜,以至于將 Codecheck 開發(fā)變成一種全職工作。
當(dāng)我深入查看 ALL_ARGUMENTS 的內(nèi)容時,我吃驚地發(fā)現(xiàn)那些參數(shù)列表會變得非常復(fù)雜。例如,在 Oracle9 i 的環(huán)境中,一個參數(shù)可以將記錄的結(jié)合數(shù)組(以前稱為表的索引)作為其類型,其中記錄的一個字段是另一個結(jié)合數(shù)組,以此類推。非常坦率地說,我實(shí)際上不希望必須編寫代碼來處理這些參數(shù)全部的可能情況。
與此相反, Codecheck 將只基于 “0 級 ” 參數(shù)來檢查和進(jìn)行分析:這是指在 ALL_ARGUMENTS 視圖中的那些項(xiàng)目,當(dāng)其出現(xiàn)在程序頭時,它們對應(yīng)于參數(shù)列表。實(shí)際上,這可能是您為分析超載而 所需 的全部要素,但是全部的具體資料可能在進(jìn)行某種其他的代碼檢查時派上用場。(在撰寫此文章時,我已確定了一種需要更多信息的情況:假如您將一個參數(shù)定義為表單 %ROWTYPE 的記錄,則 ALL_ARGUMENTS 和 DBMS_DESCRIBE 都不會提供其類型的信息。您可能必須比較那些行類型( 1 級或更多級)的 各個字段 的數(shù)據(jù)類型以識別岐義。天哪。
定義測試策略
開發(fā) Codecheck 的下一步是探究和定義測試策略。我曾提到過,我承諾編寫能夠滿足測試計劃的代碼,這種承諾影響著開發(fā)過程。例如,我在編寫如 Codecheck 等實(shí)用工具時的第一個沖動是,制作一個程序,接受一個程序包的名稱并在屏幕上顯示代碼檢查的結(jié)果。為了確定實(shí)用工具是否正確工作,我需要查看屏幕上的檢查結(jié)果并對其進(jìn)行分析。這聽起來如何?熟悉嗎?可伸縮嗎?通常這種方法甚至對非常簡單的程序都無效。
Codecheck 測試計劃無疑會有很多測試方案,而其結(jié)果并不明確。換言之,除非我記住成功對所有這些測試方案意味著什么,否則我必須依靠手動的、直觀的驗(yàn)證(先看這個窗口;然后將其與那個窗口的內(nèi)容進(jìn)行比較)。我需要用很長的時間完成一項(xiàng)測試(這是構(gòu)成責(zé)任性的一項(xiàng)相當(dāng)重要的要素),所以我很少進(jìn)行測試 — 假如曾做過測試的話。這與我的保證相違反。
我需要找到一種更快捷方便的測試方法。使用 java 的許多開發(fā)人員轉(zhuǎn)向 Junit 。 PL/SQL 開發(fā)人員則利用 utPLSQL ,一種用于 PL/SQL 開發(fā)人員的開放源單元測試框架。(申明:我是 utPLSQL 的創(chuàng)建者,盡管其他人現(xiàn)在也幫助進(jìn)行實(shí)施并制作文檔。)
我不會在本文中以很大的篇幅具體描述 utPLSQL 的起源、理論基礎(chǔ)或基本工作方式。
假如您需要了解比本文內(nèi)容更具體的情況,請訪問 http://utplsql.sourceforge.net/ 或 http://utplsql.oracledeveloper.nl 。
utPLSQL 簡介
utPLSQL 是一種用于 PL/SQL 程序的測試框架(代碼以及使用這種代碼的進(jìn)程的集合)。使用 utPLSQL ,您可以構(gòu)造包含單元測試過程的單元測試程序包,并依照 utPLSQL 的命名規(guī)范和測試機(jī)制來設(shè)計此程序包。然后您只須簡單地操作 utPLSQL 來測試程序或程序包。它運(yùn)行所有測試并自動檢測該測試是否成功或失敗。它準(zhǔn)確指出失敗的測試方案,使您更快地確定正確的測試方案并識別應(yīng)用程序中錯誤的原因。
這種框架是基于極限編程的單元測試概念 ( www.XPRogramming.com ) 以及 Junit ( Java 單元測試框架)。下面是一些該方法進(jìn)行單元測試的基本原則:
在編寫代碼前編寫單元測試。
少做編寫和更改,多做測試。
自動執(zhí)行測試和生成報告:紅綠燈式的方法。
圖 1 表示 utPLSQL 體系結(jié)構(gòu)的一個簡化視圖,它是以強(qiáng)大有效的方式自動執(zhí)行測試的一個往返旅程。下面對行程中的各站作一解釋:
調(diào)用 utPLSQL 測試過程執(zhí)行測試程序包。 utPLSQL 按照動態(tài) PL/SQL 和命名規(guī)范來運(yùn)行任何設(shè)置代碼,定位并執(zhí)行單元測試程序,并進(jìn)行必要的清除( “ 拆卸 ” 步驟)。
單元測試過程調(diào)用 “ 判定 API” ,它將測試結(jié)果與 “ 控制 ” 條件進(jìn)行比較。
判定程序?qū)⒔Y(jié)果(通過或失?。懙较旅娴慕Y(jié)果表中。
tPLSQL 測試讀出結(jié)果表中的內(nèi)容,確定該測試的狀態(tài)。
utPLSQL 根據(jù)結(jié)果作出報告,或者通過 DBMS_OUTPUT 送到屏幕,或者通過 UTL_FILE 送到一個文件。
讓我們來看一個很簡單的示例,從而大致了解單元測試程序包的內(nèi)容。假設(shè)我已經(jīng)創(chuàng)建了一個關(guān)于 SUBSTR 的封裝,答應(yīng)在開始和結(jié)束點(diǎn)之間請求一個子串(一個簡單的函數(shù))( 列表 1 )。
即使是簡單的或看似不重要的程序(如 betwnStr )也需要測試 — 而實(shí)際上我必須考慮大量的測試方案(包括 NULL 開始值、 NULL 結(jié)束值以及開始大于結(jié)束等)。 列表 2 顯示了測試程序包的一部分(全部測試包的內(nèi)容請參見 ut_betwnstr.pkb )。關(guān)于要害部分的解釋,請參見 表 1 。
也許您會想, “ 多么單調(diào)乏味!我真的必須編寫所有那些代碼,只是為了測試這個簡單的函數(shù)嗎? ” 實(shí)際上,那些在測試領(lǐng)域工作的人都知道,測試一個應(yīng)用程序所需要編寫的代碼數(shù)量經(jīng)常比應(yīng)用程序本身更多。對于這個特定的程序包和測試代碼的 utPLSQL 風(fēng)格,您在某些情況下會生成全部的測試代碼。實(shí)際上, ut_betwnstr 程序包 通過 對 utGen.testpkg_from_string 過程的調(diào)用而生成, 如列表 3 所示 。
即使您不生成測試程序包,也經(jīng)??梢哉业狡渌椒?,利用最少的代碼運(yùn)行許多基于 utPLSQL 的測試 — 這正是我用 Codecheck 所做的工作。
將 utPLSQL 應(yīng)用于 Codecheck
要利用 utPLSQL ,我需要構(gòu)建一個單元測試程序包并調(diào)用 utAssert 程序,以確定我的代碼是否通過其測試。但是,在進(jìn)行這些操作之前,我需要精心制作我的測試方案。請記住:測試方案第一,其次才是代碼。
現(xiàn)在應(yīng)該尋找靈感,暫停前進(jìn),考慮一下我所提供的實(shí)用工具。我希望它能夠驗(yàn)證什么?那些能編譯但包含歧義超載的程序包的特例是什么?我能檢測到什么情況?有效超載的示例是什么?究竟我需要測試正面和負(fù)面的因素。經(jīng)過一段時間以后,我給出以下的內(nèi)容:
有效的超載
兩個超載程序帶有不同數(shù)量的非缺省參數(shù)。
兩個超載程序帶有相同數(shù)量的非缺省參數(shù),并在數(shù)據(jù)類型方面具有足夠的差別(如 NUMBER 對 DATE )。
函數(shù)和過程具有相同名稱和參數(shù)列表,包括不帶參數(shù)的情況。區(qū)分這兩者并沒有問題,因?yàn)樗鼈冊诖a中的使用方法不同。
無效的超載
兩個超載程序具有不同數(shù)量的非缺省參數(shù),導(dǎo)致歧義的參數(shù)列表。
兩個程序具有單個參數(shù),數(shù)據(jù)類型相同但參數(shù)名不同。
兩個程序具有相同的名稱,具有單個參數(shù),但卻是同系列中不同的數(shù)據(jù)類型。
一個程序沒有參數(shù),第二個程序具有一個帶缺省值的參數(shù)。
一個程序具有 N 個參數(shù),第二個程序具有 N+1 個參數(shù),全部帶有缺省值。
一個程序具有 N 個參數(shù),其中 N-1 個參數(shù)有缺省值;第二個程序具有 N+1 個參數(shù),其中最后兩個缺省。