mockitoでリクエスト/セッションを再現
続いて、本題であるリクエストとセッションのモックの作り方を説明していきます。
Webアプリケーションでは、IN/OUTのINにあたるHttpServletRequestのリクエストパラメータのパターンは、非常に多くなります。そのため、HttpServletRequestのモックを作ってテストをすると効率が良くなりますし、テスト時間も短縮できるでしょう。私はmockitoを使う前は、リクエストパラメータのパターンをテストするために、ブラウザからテストを行っていました。けっこう時間が掛かるので根気も要りますし、たいへんな作業です。途中で飽きることもありましたが、必要なテストでしたし、そのときはそれが当たり前だと思っていたのです。今になって考えると、これは結合テストですし、かなり非効率なテストでした。
また、他にもバグを作ってしまう原因として、ユニットテスト完了後に修正が入ると、再度ユニットテストの実施が必要になることが挙げられます。この再テストは気持ち的にけっこう面倒な作業ですから、テストが甘くなってしまうことも考えられます。そういった場面でも、あっという間に再実施ができてしまうのがJUnitの魅力の一つです。また、mockitoを使うとプログラムで書くことができるので、プログラマーには楽しい仕事だと思います。
それでは前置きは終わりにして、説明に入ります。以下に記したコードはテスト対象のクラスです。メルマガ登録を行う機能を提供する、画面系クラスとなっています。
public class RegistMailMagazineBean extends BaseBean { public void process(HttpServletRequest request, HttpServletResponse response) { String customerCode = (String)request.getSession().getAttribute("customerCode"); String mailAddress = request.getParameter("mailAddress"); String mailMagazineType = request.getParameter("mailMagazineType"); if (!MailAddressValidator.isEmail(mailAddress)) { setErrorMessage("メールアドレスが正しくありません。"); setView("pre-regist-mail-magazine.jsp"); } else if (!MailMagazineValidator.isMailMagazineType(mailMagazineType)) { setErrorMessage("メルマガは1つ選択してください。"); setView("pre-regist-mail-magazine.jsp"); } else { MailMagazine.preRegist(customerCode, mailAddress, mailMagazineType); // メルマガ仮登録 MailMagazine.sendPreRegistMail(mailAddress, mailMagazineType); setView("pre-registed-mail-magazine.jsp"); } } }
このクラスは、MVCのModelにあたるクラスになります。MVCの説明については、本連載の第2回目に載っていますので、分からない方はそちらをご覧ください。唯一のメソッドのprocess()は、Controllerから呼ばれ、ビジネスロジックが記述されています。また、継承しているBaseBeanは共通メソッドを持っている簡単なクラスです。process()メソッドでは、入力チェックとメルマガの仮登録処理を主な処理としております。入力チェックが正常の場合は仮登録、および疎通メールを送信します。不正な場合にはエラーメッセージを表示します。[登録ボタン]を押すと、サーブレットが呼ばれます(メールの疎通確認が取れれば登録完了です)。
それでは、以下にこのクラスのテストコードを書きましたので、HttpServletRequestとHttpSessionをmockitoを使用して再現する方法を説明します。
@Test public void doProcessTest1() { RegistMailMagazineBean bean = new RegistMailMagazineBean(); //(1) HttpServletRequest requestMock = mock(HttpServletRequest.class); //(2) HttpServletResponse responseMock = mock(HttpServletResponse.class); //(3) HttpSession sessionMock = mock(HttpSession.class); //(4) when(requestMock.getParameter("mailAddress")).thenReturn("test@oisix.com"); //(5) when(requestMock.getParameter("mailMagazineType")).thenReturn("2"); //(6) when(requestMock.getSession()).thenReturn(sessionMock); //(7) when(sessionMock.getAttribute("customerCode")).thenReturn("00000001"); //(8) bean.process(requestMock, responseMock); //(9) assertThat(bean.getView(), is("pre-registed-mail-magazine.jsp")); //(10) }
(1)テストケースdoProcessTest1()の1行目は、テスト対象クラスのインスタンスを作っています。(2)2行目はmock()メソッドでHttpServletRequestクラスのモックを、(3)3行目はHttpServletResponseクラスのモックを、(4)4行目はHttpSessionクラスのモックを作っています。これは前に説明したCartクラスでの使い方と一緒ですね。この4行でリクエストとセッションに関するモックの作成は完了しました。非常に簡単です。
5~8行目は、こちらもCartクラスのテストケースの説明でも書きましたが、when(①戻り値を定義したいメソッド).thenReturn(②戻り値);のように書くと、モック化したメソッドの戻り値を指定できます。(5)その要領で5行目はモック化したrequestからgetParameter("mailAddress");が呼ばれると文字列の"test@oisix.com"がリターンされるようにモック化しています。(6)6行目も基本的に同じで、モック化したrequestからgetParameter("mailMagazineType");が呼ばれると文字列の"2"がリターンされるようになります。(7)7行目はモック化したrequestからgetSession();が呼ばれると、4行目で生成したHttpSessionのモックが、(8)8行目ではモック化したsessionからgetAttribute("customerCode");が呼ばれると、"00000001"という文字列が返るようにメソッドをモック化しています。
(9)10行目は、テスト対象メソッドを呼んでいます。こちらの引数にはHttpServletRequestとHttpServletResponseのモックを渡しています。これでdoProcess()メソッドは、ブラウザを使用してテストした時と同様にdoProcess ()メソッドをcallすることができます。こんな感じでリクエストとセッションのモックを使います。
(10)12行目については、テスト結果を検証しています。RegistMailMagazineBeanクラスは正常に動作するとsetView("pre-registed-mail-magazine.jsp");がcallされるので、viewの値を取得するgetするメソッド:bean.getView()の戻り値をassertThat()で検証をしています。これでmockitoを使って、リクエストとセッションを再現する方法、およびテストケースの記述方法の解説は終了です。
まとめ
今回はmockitoを使用して、モックオブジェクトの作り方~リクエストとセッションの再現方法の説明をしました。モックオブジェクトの作り方は理解していただけたでしょうか。
この記事を書くにあたって、「モックとスタブの違いはなんだ??」という疑問にもぶつかりましたが、その時によい記事を見つけたので、紹介いたします。書籍「リファクタリング」の著者としても知られている、Martin Fowler氏が書いた「モックとスタブの違い」を日本語訳している記事です。
次回は、dbunitを使ったユニットテストで使用するテストデータの作成方法をご紹介いたします。