ビヘイビア駆動とかよく分からないけど、とりあえずRSpec書きながらプロシージャを作ってみる #2
指定されたmessage_codeが見つからない場合にエラーメッセージを出してみる
前回のソースにエラー処理も足そうよ、という訳で以下のようにスペックを書きます。
it "(3) 存在しないmessage_codeを指定するとエラーメッセージとエラーコードが返る" do cursor = conn.parse(target_plsql) cursor.bind_param(':iv_message_code', 'MES_ERROR_TEST') cursor.bind_param(':iv_token1', '') cursor.bind_param(':iv_token2', '') cursor.bind_param(':iv_token3', '') cursor.bind_param(':iv_token4', '') cursor.bind_param(':iv_token5', '') cursor.bind_param(':ov_message', nil, String, 2000) cursor.bind_param(':ov_retcode', nil, String, 10) cursor.exec cursor[':ov_message'].should == "GET_MESSAGE ERROR:存在しないエラーコードが指定されました。" cursor[':ov_retcode'].should == "1" end
ついカッとなって引数を増やしてしまったので、(2)のケースも修正する必要がありやんす。。
そしてできたのが以下のPL/SQLコード。
CREATE OR REPLACE PROCEDURE GET_MESSAGE( iv_message_code IN Messages.message_code%TYPE, iv_token1 IN Messages.token1%TYPE, iv_token2 IN Messages.token2%TYPE, iv_token3 IN Messages.token3%TYPE, iv_token4 IN Messages.token4%TYPE, iv_token5 IN Messages.token5%TYPE, ov_message OUT VARCHAR2, ov_retcode OUT VARCHAR2) AS cv_program_name CONSTANT VARCHAR2(100) := 'GET_MESSAGE'; lv_ret_message VARCHAR2(2000); lv_error_message VARCHAR2(2000); BEGIN SELECT message INTO lv_ret_message FROM Messages WHERE message_code = iv_message_code; -- OUT変数にMessagesから取得した文字列を代入 ov_message := lv_ret_message; -- 正常終了 ov_retcode := '0'; EXCEPTION WHEN NO_DATA_FOUND THEN lv_error_message := cv_program_name || ' ERROR:存在しないエラーコードが指定されました。'; ov_message := lv_error_message; ov_retcode := '1'; END GET_MESSAGE;
pasta:get_message mahm$ spec -c -fs get_message_spec.rb トークンを指定せずにメッセージを取得するとき - (1) SELECT message FROM Messages WHERE message_code = 'MES000000'で「正常終了しました。」というメッセージを取得できる - (2) 「MES000000」を指定すると「正常終了しました。」というメッセージを取得 - (3) 存在しないmessage_codeを指定するとエラーメッセージとエラーコードが返る Finished in 0.158438 seconds 3 examples, 0 failures
テストも通りました、と。
そろそろ本題の「私の名前は もっこす です。」を実装する
そろそろ本題の機能(もっこす)を実装してみましょう。例のごとく先にスペックを記述します。前回までの機能とは違うので新しくdescribeを記述しますよ。
describe "トークンを1つ指定してメッセージを取得するとき" do before(:all) do # テスト前準備 apxe/apxe@xeでデータベースへ接続 conn = OCI8.new('apxe','apxe','xe') end after(:all) do # データベースからログオフ conn.logoff end it "(1)「MES000001」でトークン1に「もっこす」と指定すると「私の名前は もっこす です。」と返る" do cursor = conn.parse(target_plsql) cursor.bind_param(':iv_message_code', 'MES000001') cursor.bind_param(':iv_token1', 'もっこす') cursor.bind_param(':iv_token2', '') cursor.bind_param(':iv_token3', '') cursor.bind_param(':iv_token4', '') cursor.bind_param(':iv_token5', '') cursor.bind_param(':ov_message', nil, String, 2000) cursor.bind_param(':ov_retcode', nil, String, 10) cursor.exec cursor[':ov_message'].should == "私の名前は もっこす です。" cursor[':ov_retcode'].should == "0" end end
とりあえずエラーを出してみる。
トークンを1つ指定してメッセージを取得するとき - (1)「MES000001」でトークン1に「もっこす」と指定すると「私の名前は もっこす です。」と返る (FAILED - 1) 1) 'トークンを1つ指定してメッセージを取得するとき (1)「MES000001」でトークン1に「もっこす」と指定すると「私の名前は もっこす です。」と返る' FAILED expected: "私の名前は もっこす です。", got: "GET_MESSAGE ERROR:存在しないエラーコードが指定されました。" (using ==) ./get_message_spec.rb:105:
前に実装したエラーメッセージが表示されてて良い感じですね。
message_code | message | token1 | token2 | token3 | token4 | token5 |
MES000001 | 私の名前は &NAME です。 | &NAME |
上記のような形でMessagesにデータ登録して実装してみましょう。
INSERT INTO "APXE"."MESSAGES" (ID, MESSAGE_CODE, MESSAGE, TOKEN1) VALUES ('2', 'MES000001', '私の名前は &NAME です。', '&NAME') コミットは成功しました
そしてエラー処理とかは考えずに、指定箇所が置換されるだけのソースを書いてみます。
create or replace PROCEDURE GET_MESSAGE( iv_message_code IN Messages.message_code%TYPE, iv_token1 IN Messages.token1%TYPE, iv_token2 IN Messages.token2%TYPE, iv_token3 IN Messages.token3%TYPE, iv_token4 IN Messages.token4%TYPE, iv_token5 IN Messages.token5%TYPE, ov_message OUT VARCHAR2, ov_retcode OUT VARCHAR2) AS cv_program_name CONSTANT VARCHAR2(100) := 'GET_MESSAGE'; rec_message Messages%ROWTYPE; lv_ret_message VARCHAR2(2000); lv_error_message VARCHAR2(2000); BEGIN SELECT * INTO rec_message FROM Messages WHERE message_code = iv_message_code; lv_ret_message := rec_message.message; -- TOKEN1が指定されている場合に埋め込み処理 IF iv_token1 IS NOT NULL THEN -- レコードに設定されているTOKEN1の値がmessage中に設定されているか? IF REGEXP_LIKE( rec_message.message, rec_message.token1, 'i') = TRUE THEN lv_ret_message := REGEXP_REPLACE( lv_ret_message, rec_message.token1, iv_token1 ); END IF; END IF; -- OUT変数にMessagesから取得した文字列を代入 ov_message := lv_ret_message; -- 正常終了 ov_retcode := '0'; EXCEPTION WHEN NO_DATA_FOUND THEN lv_error_message := cv_program_name || ' ERROR:存在しないエラーコードが指定されました。'; ov_message := lv_error_message; ov_retcode := '1'; END GET_MESSAGE;
コンパイルが通ったのでスペック実行!
pasta:get_message mahm$ spec -c -fs get_message_spec.rb トークンを指定せずにメッセージを取得するとき - (1) SELECT message FROM Messages WHERE message_code = 'MES000000'で「正常終了しました。」というメッセージを取得できる - (2) 「MES000000」を指定すると「正常終了しました。」というメッセージを取得 - (3) 存在しないmessage_codeを指定するとエラーメッセージとエラーコードが返る トークンを1つ指定してメッセージを取得するとき - (1)「MES000001」でトークン1に「もっこす」と指定すると「私の名前は もっこす です。」と返る Finished in 1.247753 seconds 4 examples, 0 failures
軽やかに通りました。
ここまでRSpecを使ってきて思うこと
やはりスペックファイルの書き方の方針に悩みます。。
ノープランで書いてきましたが、どんな単位でdescribeを書いていくべきか、その中のitはどのように区分けするか、この辺は方針として持っておかないと混沌としていきますね。itの書き方の整理の付け方も難しいです。
そもそもクラスの振る舞いを記述するためのものであってPL/SQLのプロシージャの振る舞いを記述する想定はないのですから、ノープランでやれば混沌とするのがむしろ当たり前ではありますが。。
次回はこの混沌としたSpecファイルを少し整理してみようと思います。
続き:ビヘイビア駆動とかよく分からないけど、とりあえずRSpec書きながらプロシージャを作ってみる #3 まとめ - ランバダ