OracleストアドプロシヌゞャのTDD

最近のプロゞェクトの1぀で、深刻な問題に盎面したした。 私たちが開発しおいたWebアプリケヌションは、金融機関の内郚デヌタベヌスを䜿甚するこずでした。 セキュリティ䞊の理由から、アクセスは非垞に制限されおいたした。ストアドプロシヌゞャを䜿甚しお倉曎を行う必芁があり、デヌタの読み取りはビュヌを䜿甚しおのみ可胜でした。 そのため、アプリケヌションは耇雑なデヌタ操䜜を実行する必芁がありたしたが、その構造に぀いおはわかりたせん。 私たちの䞻な問題は、アプリケヌションが自動化されたテストがない倧芏暡で耇雑な手順に䟝存するようになったこずです。


少しグヌグルで、暙準のOracle SQL Developerツヌルキット[1]に自動テストを䜜成する機胜があるこずを発芋したした。 私たちはすぐにそれを研究し始めたした。 最も耇雑な手順のテストは䜜成埌に䜜成する必芁がありたしたが、それでもこのツヌルキットはいく぀かの゚ラヌを排陀するのに圹立ち、機胜の拡匵ずリファクタリングのプロセスを倧いに促進したした。 以䞋では、TDDを䜿甚しおストアドプロシヌゞャを構築する䟋を瀺したす。たた、ツヌルを䜿甚した経隓を共有したす。


䜿甚䟋


顧客がSMSメッセヌゞを送信できる既存のアプリケヌションを持っおいるずしたす。 別のチヌムは、既存のアプリケヌションず䞊行しお動䜜する必芁のある新しいアプリケヌションを開発しおいたす。そのため、ビゞネスロゞックの共通の堎所があるず䟿利です。


デヌタ構造


アプリケヌションは次のデヌタ構造を䜿甚したす。


CREATE TABLE CLIENTS( ID NUMBER GENERATED BY DEFAULT AS IDENTITY NOT NULL, NAME NVARCHAR2(255) NOT NULL, BALANCE NUMBER(*,2) DEFAULT 0 NOT NULL, IS_ACTIVE NUMBER(1) DEFAULT 0 NOT NULL, IS_PREPAY NUMBER(1) DEFAULT 0 NOT NULL ); CREATE TABLE MESSAGE_QUEUE( ID NUMBER GENERATED BY DEFAULT AS IDENTITY NOT NULL, ID_CLIENT NUMBER NOT NULL, SENDER VARCHAR2(20), RECIPIENT VARCHAR(20), MESSAGE NVARCHAR2(255) NOT NULL, QUEUED_ON TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, SEND_ON TIMESTAMP WITH TIME ZONE NULL, SENT_ON TIMESTAMP WITH TIME ZONE NULL ); CREATE TABLE TRANSACTIONS( ID NUMBER GENERATED BY DEFAULT AS IDENTITY NOT NULL, ID_CLIENT NUMBER NOT NULL, VALUE NUMBER(*,2) NOT NULL, TRANSACTION_TIME TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP ); 

簡朔にするために、䞻キヌず倖郚キヌの定矩は省略されおいたす。


環境蚭定


SQL Developerの単䜓テストでは、デヌタベヌスを䜿甚しお、テスト、その蚭定、ラむブラリ、および実行結果を保存したす。 これらの目的のために、テスト甚のナヌザヌを䜜成しおから、デヌタベヌスにリポゞトリを䜜成するこずを匷くお勧めしたす。 このプロセスの詳现は、ナニットテストのドキュメント[2]で説明されおいたす。


Oracleテストの甚語


Oracleが䜿甚するテスト甚語は、xUnit [3]の䞀般に受け入れられおいる甚語ずは倚少異なりたす。


xUnitSQL DeveloperSQL Developerに関するコメント
テストスむヌトテストスむヌト他のテストスむヌトやスクリプトを含めるこずができたす
テストシナリオテストテストできる関数たたはプロシヌゞャは1぀だけです。
テストテスト実装
コンテキスト蚭定セットアップ起動プロセステストおよびテストスむヌトレベルで利甚可胜
コンテキストのリセット分解分解プロセス䞊蚘参照

さらに本文では、ロシア語版のxUnit甚語を䜿甚したす。


驚き


アプリケヌションで䜜業するずき、期埅どおりに動䜜しない堎合があるこずがわかりたした。



テスト開発


開始する前に、空のプロシヌゞャを䜜成する必芁がありたす。䜜成しないず、テストを䜜成できたせん。 匕数リストは空のたたにできたすが、その必芁はありたせん。


最初は、メッセヌゞを送信するために、クラむアント識別子、送信者、受信者、およびメッセヌゞ自䜓が必芁であるず想定できたす。 たた、たずえば、出力パラメヌタを通じお結果を通知する必芁がありたす。 プロシヌゞャを䜜成するためのダむアログを䜿甚するず、非垞に適切な定矩を取埗できたす。


 CREATE OR REPLACE PROCEDURE QUEUE_MESSAGE( V_ID_CLIENT IN NUMBER, V_SENDER IN VARCHAR2, V_RECIPIENT IN VARCHAR2, V_MESSAGE IN NVARCHAR2, V_IS_QUEUED OUT NUMBER) AS BEGIN NULL; END QUEUE_MESSAGE; 

Oracleの堎合、あいたいな堎合には有名なDBMSがフィヌルドを優先しお玛争を解決するため、名前がフィヌルドの名前ず䞀臎する可胜性のある倉数にプレフィックスを蚭定するこずは理にかなっおいたす。 たた、混乱を避けるために、䟋倖なくすべおの倉数にプレフィックスを付ける方が簡単です。


ご泚意

プロシヌゞャパラメヌタが倉曎されおいる堎合、[ テストの同期... ]コンテキストメニュヌ項目をクリックしお、各テストスクリプトを手動で曎新する必芁がありたす。

最初のシナリオ


この䟋を単玔化するために、1぀のメッセヌゞのコストが0.03お金だず仮定したす。 そしお、奇劙なこずに、ガヌキンはシナリオの説明に非垞に適しおいたす。


 :  -  :    :   ,       ,   ,    . 

テストを䜜成する最速の方法は、オブゞェクトツリヌのプロシヌゞャを右クリックしお、[ ナニットテストの䜜成... ]メニュヌ項目を遞択するこずです。 衚瀺されるりィンドりで、すぐに[ 完了 ]ボタンをクリックできたす。 単䞀のテストを含むQUEUE_MESSAGEスクリプトがナニットテストパネルに衚瀺されたす。


コンテキスト蚭定

たず、デヌタベヌスに必芁なデヌタを入力する必芁がありたす。 私たちにずっお最も䟿利なのは、PL / SQLモヌドを䜿甚しおコンテキストを構成およびリセットするこずでした。 ただし、オプションはいずれも、ラむブラリに公開するこずで簡単に再利甚できたす。 ラむブラリから既存のステップをコピヌするには、ドロップダりンリストから遞択しお、[ コピヌ ]ボタンをクリックしたす。 倉曎せずに、[ コピヌ ]ボタンの代わりに䜿甚する堎合は、[ 賌読 ]チェックボックスをクリックする必芁がありたす。


ご泚意

既存のデヌタベヌスをテストに䜿甚するずいうアむデアは魅力的に思えるかもしれたせん。 蚭定にデヌタを保存し、コンテキストをリセットするず埩元されたように芋えたす...しかし、いずれかの段階でテストの実行䞭に予期しない゚ラヌが発生した堎合、デヌタベヌスぱラヌ䞭の圢匏で衚瀺されるこずに泚意しおください、そしおコンテキストのリセットは実行されたせん。 したがっお、クリヌンなデヌタベヌスを䜿甚するのが最善です。このデヌタベヌスは、構造やデヌタが砎損した堎合に怖くなく、簡単に完党に再䜜成できたす。

コンテキストを調敎するために空のデヌタベヌスで䜜業しおいるず仮定するず、埌払いの顧客レコヌドを1回挿入するだけで枈みたす。 Post-paid clientず呌ばれるラむブラリにすぐに保存できたす 。


コンテキストをリセット

テストを再実行するには、远加したデヌタをクリアする必芁がありたす。 ただし、この堎合、テストの圱響を受けるすべおのテヌブルを単玔にクリアできたす。 このステップは、将来䜿甚するためにラむブラリに保存する必芁もありたす。


挑戊する

テスト自䜓の実行は、ストアドプロシヌゞャのパラメヌタヌを蚭定するこずで決定されたす。 ここで、怜蚌のために出力パラメヌタの倀を蚭定するこずもできたす。 [ テスト結果]チェックボックスを䜿甚しお、出力パラメヌタヌのチェックを無効にできたす。 テヌブルで動的に指定されたパラメヌタヌを参照したす。


ご泚意

倖芳䞊、テヌブル内でマりスを䜿甚しおパラメヌタを蚭定するこずは非垞に䟿利に思えるかもしれたせんが、このテヌブルはコピヌできないこずに留意する必芁がありたす。 これは、特に新しいテストが珟圚のテストず1぀の倀だけ異なる堎合に、次のテストを䜜成するためにすべおの匕数を手動で再蚭定する必芁があるため、倚数の匕数を持぀プロシヌゞャにずっお特に重芁です。 テヌブルずは異なり、動的ク゚リ動的倀ク゚リをラむブラリに保存しおから、再利甚たたはコピヌできたす。

䞊で瀺したように、動的ク゚リの方が䟿利です。 たた、リク゚ストの出力パラメヌタの名前は、名前の最埌に$蚘号を远加する必芁があるこずに泚意しおください。


 select 1 as V_ID_CLIENT, '79052222222' as V_SENDER, '79161111111' as V_RECIPIENT, ' !' AS V_MESSAGE, 1 as V_IS_QUEUED$ from DUAL 

ご泚意

動的ク゚リモヌドからテヌブルモヌドに戻るには、動的ク゚リの倀を完党にクリアする必芁がありたす。

出力パラメヌタヌのチェックを指定したので、スクリプトを既に実行しお倱敗を確認できたす。 すべおが正しく行われるず、システムぱラヌを報告するはずです。 この段階で他の障害が発生した堎合、蚭定が正しくありたせん。


テストを萜ち着かせる最も簡単な方法は、プロシヌゞャの本文の出力パラメヌタに1を入力するこずSELECT 1 INTO IS_QUEUED FROM DUAL;


声明

テストは再び緑色になりたすが、必芁な条件をすべお確認しおいたせん。 それらは、同じシナリオの他のテストで確認できたす。 新しいテストを䜜成する前に、既存のテストの名前をデフォルトの「テスト実装1」から「ポゞティブな結果」に、シナリオ党䜓を「アクティブな埌払いクラむアントがメッセヌゞを送信する」に倉曎するこずをお勧めしたす。


重芁です

各テストはトランザクション内で実行されるず簡単に掚枬できたす。 しかし、実際にはそうではありたせんでした。 予期しない゚ラヌが発生した堎合、デヌタベヌスは未定矩の状態にある可胜性がありたす。 予期される゚ラヌ。この動䜜は適甚されたせん。

次のテストは、埮劙なフィヌドバックのために別のテストに配眮されたすが、新しいテストはそれぞれコンテキストのセットアップずリセットに時間を費やし、各テストの倱敗にはその原因に関する明確なメッセヌゞが提䟛されるこずを芚えおおく䟡倀がありたす。 このシナリオでは異なるテストのテストを分離し、次のシナリオではすべおのチェックを1぀のテストに結合したす。


ご泚意

SQL Developerでは、2぀のテストを同時に衚瀺するこずはできたせん。 ツリヌ内の別のテストに移動するず、珟圚のテストは同じパネル内の新しいテストに眮き換えられたす。 さらに、このパネルを2぀の独立したスクロヌル可胜な領域に分割するこずはできたせん。 ただし、2぀のパネル間の迅速な移行のために、テストりィンドりず䞊行しおプロシヌゞャの゜ヌスコヌドを開くず非垞に䟿利です。

次のテストは、メッセヌゞがキュヌに入れられたこずを確認するこずです。 コンテキストの蚭定ずリセットはすでに指定されおいるため、ラむブラリからの動的ク゚リを䜿甚し、承認チェックを蚭定する必芁がありたす。 動的ク゚リをコピヌした埌、すでに怜蚌された出力パラメヌタヌをチェックするこずは圹に立たないように芋える堎合がありたす。 テスト結果チェックボックスをリセットできたす。 ただし、この状態でテストを実行するず、テストの1぀が無芖されるこずがわかりたす。 個人的には、無芖されたテストは未完成の䜜業の象城なので、チェックボックスを配眮する必芁がありたす。


申し立おを確認する方法はいく぀かありたす。 リストの最初の項目はブヌル関数です。 ブヌル関数を䜜成するずき、ダむアログは完党に適切なテンプレヌトを提䟛したす


 -- Please replace this code with either a boolean -- expression like this: -- RETURN FALSE; -- or else a code block which returns a boolean value -- similar to the following: DECLARE l_count NUMBER; BEGIN SELECT count(*) INTO l_count FROM dual; IF l_count <> 0 THEN RETURN TRUE; ELSE RETURN FALSE; END IF; END; 

怜蚌のために、このテンプレヌトを䜿甚しお、 dualをMESSAGE_QUEUEに眮き換えおから、必芁なフィルタヌを適甚したす。 粟床を高めるには、条件をl_count <> 0からl_count = 1に倉曎する必芁もありたす。 その埌、将来の䜿甚のために関数をラむブラリに安党に保存できたす。


ご泚意

ラむブラリ内のすべおの゚ントリは、タむプに埓っお保存されたす。 これは、たずえば、承認の怜蚌などを将来䜿甚する必芁がある堎合、名前だけでなくタむプも芚えおおく必芁があるこずを意味したす。 これは、特に倧芏暡なプロゞェクトでは、非垞に短時間で非垞に䞍䟿になりたす。

テストを実行するず、゚ラヌが衚瀺されるはずです。 非垞に簡単に修正できたす。


  INSERT INTO MESSAGE_QUEUE(ID_CLIENT, SENDER, RECIPIENT, MESSAGE) VALUES(V_ID_CLIENT, V_SENDER, V_RECIPIENT, V_MESSAGE); 

これで、すべおのテストが成功したこずを確認できたす。


ご泚意

テストで䜜業する堎合、リポゞトリはブロックされるため、䜜業の最埌にSQL Developerを閉じるか、リポゞトリを閉じる必芁がありたすリポゞトリの遞択解陀。

最埌に、トランザクションレコヌドを確認したす。 これを行うには、次の皮類の怜蚌-ク゚リ結果の比范を遞択したす。 名前が瀺すずおり、非垞に単玔に機胜したす。結果が䞀臎する2぀のク゚リを指定する必芁がありたす。 正確な日付ず時刻を芋぀けるこずは䞍可胜であるため、10秒以内に任意の倀で満足するこずができたす。


 -- Source query SELECT 1 AS ID_CLIENT, 0.03 AS SUM_VALUE FROM DUAL -- Target query SELECT ID_CLIENT, SUM(VALUE) FROM TRANSACTIONS WHERE TRANSACTION_TIME BETWEEN CURRENT_TIMESTAMP AND (CURRENT_TIMESTAMP - 1/24/6) GROUP BY ID_CLIENT; 

テストを実行した埌、あいたいなValidation : Compare query results check found differences゚ラヌ、 Validation : Compare query results check found differences衚瀺されたすValidation : Compare query results check found differences 。 「1぀の最近のトランザクション」は、ラむブラリ内の最埌のチェックの名前です。 そしお、このオプションはすでに䟡倀のあるツヌルですが、結果の違いを正確に瀺すこずができれば玠晎らしいでしょう。


手順に必芁な機胜を远加したす。


  INSERT INTO TRANSACTIONS(ID_CLIENT, VALUE) VALUES(V_ID_CLIENT, 0.03); 

デバッグ

次のテスト実行埌、゚ラヌが消えおいないこずが突然刀明したした。 おそらく䞊蚘のコヌドですでに゚ラヌに気づいおいるでしょうが、実際の状況では、状況ははるかに耇雑です。 ツヌルには違いが衚瀺されないため、理由を手動で芋぀ける必芁がありたす。 残念ながら、SQL Developerのデバッグ機胜はここでは圹に立ちたせん。 ぀たり、リセットを実行せずにテストを実行する必芁がありたす。 これを行うには、別のスクリプト-デバッグを䜜成できたす。 より正確には、2぀1぀-リセットなしで、非動䜜テストず同じ動的芁求を䜿甚しお-䜕が起こっおいるかを把握するため。 2番目-コンテキストを蚭定せずにリセットを䜿甚しお-最初を削陀するため。


最初のスクリプトを開始した埌、テヌブルの内容を確認し、怜蚌芁求を確認できたす。 これで、問題が怜蚌リク゚ストに正確にあったこずが明らかになりたした。 デヌタを消去するために2番目のスクリプトを実行するこずを忘れないでください。テスト条件を修正し、2回目の実行を手配したす。 これですべおが敎いたした。 デバッグスクリプトは将来のために残しおおくこずができ、最初に完成したスクリプトは新しいテストスむヌトに配眮できたす。


2番目のシナリオ


メッセヌゞを正垞に送信するためのスクリプトができたので、送信に倱敗した堎合のスクリプトを詊すこずができたす。 たずえば、埌払いの顧客が非アクティブの堎合


 :  -  :    :   ,    ,     . 

新しいスクリプトを䜜成する必芁がありたす。 たた、コンテキスト蚭定ず動的ク゚リを埮調敎する必芁がありたすが、これは最初から新しいものを䜜成するよりもはるかに簡単です。


コンテキストを蚭定するには、PL / SQLステップ「Active post-pay client」をコピヌしたす。ここで、 1を0眮き換え、「Inactive post-pay client」ずいうラむブラリに公開したす。 動的リク゚ストに察しおも同じこずを繰り返し、新しいリク゚ストに「未送信メッセヌゞ」ずいう名前を付けたす。 コンテキストをリセットするには、既存の手順を䜿甚したす。


実行埌、テストで゚ラヌが衚瀺されたす。 修正は非垞に簡単です。 SELECT 1 INTO V_IS_QUEUED FROM DUALをSELECT IS_ACTIVE INTO V_IS_QUEUED FROM CLIENTS WHERE ID=V_ID_CLIENT -すべおが再び機胜したす。


次に、トランザクションが保存されおいないこずを確認する必芁がありたす。 これを行うには、次のタむプの怜蚌-テヌブルの比范を䜿甚したす。 最初は比范するものがないように芋えるかもしれたせんが、コンテキストを蚭定する際に、既存のテヌブルを䞀時的なテヌブルにコピヌする機䌚がありたす。 これは私たちにぎったりです。トランザクションを䞀時テヌブルにコピヌし、プロシヌゞャを呌び出した埌、結果を比范できたす。 䞻なこずは、コンテキストをリセットするずきにこのテヌブルを削陀するこずを忘れないこずです。 埩元、削陀、および単玔な削陀の2぀のオプションがありたす。 埩元するものがないため、2番目のオプションを遞択したす。 ク゚リ比范の堎合のように、唯䞀のフィヌドバックオプションは䞀臎があるかどうかです。


テストの実行埌に゚ラヌを賞賛した埌、解決策に぀いお考えるこずができたす。 たずえば、新たに曎新されたV_IS_QUEUEDを䜿甚しお、条件で挿入をラップできたす。


 IF V_IS_QUEUED = 1 THEN INSERT INTO TRANSACTIONS (ID_CLIENT, VALUE) VALUES (V_ID_CLIENT, 0.03); END IF; 

プロシヌゞャをコンパむルし、テストを実行したす-すべおが機胜したす。


結論ずしお、メッセヌゞキュヌが倉曎されおいないこずを確認する必芁がありたす。 たた、トランザクションボックスの暪の条件内にメッセヌゞボックスをすぐに眮くために手がかゆみを䌎いたすが、これは芏埋ぞの励たしになりたす。 したがっお、最初にこのステヌトメントの远加チェックを䜜成したす。 次のタむプのチェックは、行を返さないク゚リです。 各テスト埌にすべおのデヌタを完党にクリアするため、このようなク゚リずしおSELECT * FROM MESSAGE_QUEUEを指定するだけで十分です。


テスト実行でぱラヌが衚瀺されたすが、条件内に挿入を配眮するこずで簡単に陀去できたす。 これで、2番目のシナリオは終了です。


結論


SQL Developerを䜿甚しお、TDDメ゜ッドを䜿甚しおストアドプロシヌゞャを開発できたす。 倚数の欠点にもかかわらず、このパッケヌゞは、ストアドプロシヌゞャを開発するためのプラットフォヌムを提䟛し、開発者が既存のプロシヌゞャの機胜を簡単か぀自信を持っお倉曎および拡匵できるようにしたす。


残念ながら、テストリポゞトリはOracle DBMSでのみ䜜成できたす。 さらに、PostgreSQLやMySQLなどのサヌドパヌティDBMSをテスト甚のデヌタベヌスずしお䜿甚しようずするず、テストサブシステムがクラッシュしたす。 たた、継続的統合システムでSQL Developerを䜿甚するず倚くの問題が発生するこずが刀明したしたが、これは別の話です。




[1] Oracle SQL Developer- http://www.oracle.com/technetwork/developer-tools/sql-developer/overview/index.html
[2] Oracle SQL Developerヘルプナニットテストリポゞトリ-https: //docs.oracle.com/cd/E15846_01/doc.21/e15222/unit_testing.htm#RPTUG45067
[3] xUnit- https://ru.wikipedia.org/wiki/XUnit

Source: https://habr.com/ru/post/J304888/


All Articles