【初級~中級】【JUnitテスト】Mockを使いこなそう
モックとは何か(基本)と、
モックを使いたい人全員に伝えたい、JUnit5で使用する時の重大な注意点を記しておきます。 基本は分かってるぜ!って方は「さいごに」だけ読んでください。PowerMockのバージョンについての注意事項を書いています。
Mock(モック)とは(基本)
(基本なので、ご存知の方は読み飛ばしてください)
Javaは様々なクラス、コンポーネントで構成されます。 コンポーネントを分けることで、様々な機能を汎用的に利用することが出来るから。 でもコンポーネントを分けたことで、テストをするときに、ある問題が出てきます。
例えば…
クラスAをテストしたい! でもクラスAは、複雑な処理をするクラスBとクラスC、まだ実装途中のクラスDを呼び出す処理を持つ、といった場合。(けっこうよくある事象です) クラスAのテストがメインのはずなのに、なぜかクラスDの実装を待ち、更にクラスB、Cの複雑な処理を再現。ようやくクラスAのテストができる!と、テスト毎に繰り返していくのは大変です。
そこで、Mock(モック)やStub(スタブ)、Spy(スパイ)といった考え方が出てきます。
モック、スタブ、スパイは異なるものですが、ざっくり言うと、3つとも、テスト対象クラスから呼ばれるオブジェクトの処理の代わりに、必要な返却値だけをテスト対象クラスに提供してくれる便利なツールです。(3者の詳しい違いを知りたい方はググってください。気が向いたら記事にします)
今回はこのうち、モックに焦点を当てていきます。
モックとは、あるクラスのメソッドが呼ばれたときの返却値を予め決めておくことで、そのメソッドの処理をいちいち再現せずに、テストしたいクラスのテストを可能にしてくれる仕組みです。
ClassAをテストする時に、ClassBのXXXメソッドを呼んだら、その瞬間にxxという値を自動的に返す(XXXメソッドの処理を実行するのではない)というかんじ。
こうすることで、クラスA のテストをするときに、いちいち他のクラスの処理を律儀にやらなくて済みます。
モック関連のライブラリ
モックの役割やその利便性はご理解いただけたでしょうか。
javaでは、モックを作るために便利なライブラリがそろっています。 代表的なのは以下4つ。
- Mockito(多分これが一番ノーマル)
- easy-Mock(Mockitoの前からあった?もの)
- PowerMock(Mockitoが進化したっぽいやつ)
- jMockito(一応ノーマルだがバージョンによる機能変化が激しいため、個人的にはお勧めしない)
各ライブラリの使い方や違いなどは別の記事で書きます。
なお、どれか1種類だけを使ったり、全部使ったりすることはあまりなく、 用途に応じて2種類くらいを使い分けることが多いみたいです。
さいごに(重要なこと)
こちらは初心者さんというより、モックを使おうという方全員向けのお話です。
Mockitoは、JUnit4, 5ともに使えます。PowerMockも、JUnit4、5とも使えます。
が、JUnit5は、PowerMockの代表的な使い道である、private・static・finalメソッド等のモック化には対応していません。その他の一部の機能だけ対応しているようです。
(2019年6月現在の話です。ちなみにJUnit4では使えます。)
私はこれになかなか気づかず、JUnit5×PowerMockでどうにかテストしようと丸2日費やしました…orz(それにしても本当に何のためのPowerMockなのか(#^ω^))
そのためテストでは、使用しているJUnitのバージョンを必ず確認するようにしてください。(テストに限らずライブラリ使用時は確認すべきですが特に!)
それでは次回以降の記事で、具体的にMockitoなどの使い方を説明していきます('ω')
【Spring初心者向け】Controller、Service、RepositoryとDispathcerServlet
今回は、Springの基本的な構造についてまとめます。
なお片仮名用語には念のため注釈をつけています。見なくても分かるよ!という方はスルーしてください。
1.はじめに
SpringMVCという名前がついていますが、実態はM・V・Cの3つだけで出来ているわけではありません 。
確かに、おおざっぱに考えるなら、Springの構造はM・V・Cの3つに分けられます。が、その3つの理解だけだと実務について行けないのも事実。
そこで、Springアプリケーションの構造を、もう少し詳しく見て行こうというのがこの記事の主旨です。
2.全体像
クライアントからリクエスト*1が送られてきて、そのリクエストに沿った結果*2を返却するまでの、全体の処理の流れです。
通常、実務では、1つのリクエストに対して、以下のように多くの処理が行われることが普通です。
では、クライアントからリクエストが送られて以降の、各処理の流れの詳細を見ていきましょう。
3.各処理の詳細
①②事前処理、DispatcharServletの呼び出し
まずクライアントからリクエストが送られてきます。(①)
ただリクエストがすぐにControllerに行くわけではなく、
ということをしています。(②)
詳しくは後日扱います。ここでは、Controllerの処理の前に、準備が行われるんだな~程度に、頭の片隅に置いておいてもらえればOKです。
③④Controller⇒Serviceの呼び出し
いよいよControllerが呼び出され、メイン処理が始まります。(③)
といっても、Controller自身がメイン処理をすべて実行するのではありません。実行司令官のような役割を果たします。具体的には、Serviceという、実際に処理をしてくれるオブジェクトを呼び出します。(④)
Serviceは、基本的にとある処理の実行に特化したクラスとして実装されることが多いので、1つのControllerが複数のServiceを呼び出すこともよくあります。
イメージはこんなかんじ。Controllerが司令官、Serviceが実行者のような役割。
⑤⑥Serivice⇒Repositoryの呼び出しとDBアクセス
通常、Repository(リポジトリ)はDBアクセスを担う専門担当として、Serviceから呼び出されます。*4(⑤)
RepositoryはSeriviceからの呼び出しを受けて、データベースにアクセスするためのSQLを実行します。(⑥)
なお上の図にはRepository、Serviceがそれぞれ1つしか書かれていませんが、大きなプロジェクトでは基本的にどちらも複数存在しています。そのため、各Serviceが、必要なDBアクセス処理をしてくれるRepositoryを呼び出します。
⑦⑧DB⇒Repository⇒Serviceへの結果返却
ここからは、呼び出された結果を上位層に返却していく流れとなります。
先ほどとは逆の順をたどっていきます。
まずDBからは、SQL命令文の実行結果をRepositoryに返します。(⑦)
例えば男性会員のレコードを探してくるようSQLで求められたとすると、返される値は男性会員のデータとなります。
その後Repositoryは、DBから得た結果を、呼び出し元であるServiceに返します。(⑧)
⑨Service⇒Controllerへ結果返却
Serviceは更に、Repositoryから得た結果(つまりDBアクセスの結果)や、Service自身が行った処理の結果などを、呼び出し元であるControllerに返していきます。(⑨)
⑩Controller⇒DispathcerServlet
Controllerは、Serviceから受け取った結果とともに、次にクライアントに映し出すViewの名称*5の名称を、DispathcerServletに返却します。(⑩)
⑪View名解決
DispathcerServletは、Controllerから受け取ったView名称をもとに、クライアントに返すべきViewを見つけます。(⑪)
このViewがクライアントに返却され、画面に映し出されるものとなります。
⑫⑬後始末とクライアントへの結果(レスポンス)返却
さて、最初に、メイン処理とは別に、事前準備の処理を行っていたのを覚えているでしょうか。最後も最初と同様、メイン処理を全て終えた後に、後処理が行われます。(⑫)
こちらも詳細は後日別の記事で紹介します。今は、全ての処理終了後、レスポンス返却前に後処理が行われるんだな~と思っておいてください。
後処理まで無事に終了したら、リクエストに対する結果(View)が、やっとクライアントに返されます。(⑬)
4.さいごに
今回紹介したように、クライアントからリクエストが送られてきて、それに対するレスポンスを返却するまでには、長い道のりをたどります。
それぞれの処理の詳細は、順次、記事にしていく予定です。
*1:リクエスト:このページが見たい、「送信」ボタンを押したい等の、ユーザーの要望だと考えてください
*2:結果(レスポンス):次のページを表示する、送信完了の文字を出す等、ユーザーの要望に対する対応の結果だと考えてください
*3:メソッド:プログラムが行う処理内容
*4:
Repository(リポジトリ)とは、データベースにアクセスするときに呼び出されるものですが、RepositoryはDBアクセスの必要条件ではありません。
旧来のアプリケーションではRepositoryが存在せず、Serviceが直接データベースにアクセスする役割も担っていました。
ただそれだと、Serviceの役割が大きすぎて負荷がかかってしまいます。
負荷を分散させるために、Seriviceを2つに分けて、
・DBアクセス以外の色んな処理を行うパート
・DBアクセスを行うパートを呼び出す処理も行う(現在Serviceと呼ばれている部分)
DBアクセスを行うパート(Repository)
という2つのパートが存在しているわけです。
*5:View名:厳密には色々ありますが、ここでは画面のことだと思ってください。例えばもともと次のページを開きたいというリクエストがあったなら、次のページの画面の名称(例:gamen01)などです
【Spring初心者向け】アノテーションの付与方法
記念すべき初回記事です!
アノテーションについてまとめます。
Spring始めたばかりの頃、筆者は、アノテーションがただのコメントと同じようなものだと思っていました(汗)
実際は非常に重要な役割を持っており、使いこなせないとかなーーりしんどいということを、現場入って3日目に気づきました。
気づきを踏まえ、初心者目線で丁寧に解説します。(間違いなどありましたらコメントください)
なお、アノテーションそのものの説明はいいから、付与方法だけ教えてくれ!っていう方は4.アノテーション付与のための設定方法にお進みください。
1.アノテーションって何?
そもそもアノテーションって何?ってなりますよね。で、ググると、注釈とか出てくる。
でもSpringにおけるアノテーションは、単にコードを分かりやすくするための注釈・コメントに留まらず、プログラムに対して「自動的にこの作業してね」っていう命令みたいなものとして使われているようです。
その命令の内容は、どのアノテーションを使うかによって決まります。
イメージはこちら。見た目的には、「@」から始まる比較的短めの英語。
この図だと、@NotNull というアノテーションが使われています。
@NotNullは、名称から何となくイメージできるかもしれませんが、入力時に「null(何も入力されていない)」のはダメ、すなわち入力必須項目だよっていうのを表しています。
2.具体例×2
@Controller
これを付与したクラスはMVCのコントローラ(※1)クラスとして扱うよ、っていうのを、プログラムに示すためのやつ。
(※1 コントローラとは…プログラムの指示出しリーダーみたいなやつだと思ってもらえればひとまずOKです。後日詳細記事執筆予定)
これが無いとき、プログラムからすると、どのクラスがコントローラなのか分からないため、プログラムが動かなくなったり、意図した動作にならなかったりします。
@RequestMapping
もう1つご紹介。
これは、クライアントからのリクエストに基づき、コントローラ内のどのメソッド処理を行うか決めるためのもの。
イメージとしてはこんな感じ↓
@RequestMappingを使うと、クライアント側からの要求と、その要求に対応するメソッドを結び付ける処理を、簡単にやってくれます。
その他にもたくさんありますが、ググれば多くヒットするので、気になる方は探してみてください。
3.アノテーションが使われる理由
アノテーションとは何か、何となくイメージ出来たでしょうか?
ひとまず、アノテーションが、「この作業をしてほしい」を、プログラムに簡潔に伝える役割を持っていることを理解してもらえれば十分だと思います。
次に、そもそもアノテーションはなぜ使われているのか?どういうメリットがあるのか?、考えていきます。
実はアノテーションを使わずとも、処理の実装ができるものも多いようです。
ただ実装がとても難しくなったり、煩雑になったり、アノテーションを知らないと実務についていけなかったりと、「使わない」理由はなさそうです。
(時と場合によっては不要なのかもしれませんが、アノテーションとは?を知りたいと心者レベルのうちは、そこまで考えなくても良いはず(筆者は考えてな…ごにょごにょ))
では、アノテーションを使うことにどんなメリットがあるのか。2点ご紹介です。
- 実現したいことを、簡単に実装できる
- 簡潔で分かりやすく、誰がコードを書いても同じになる
1.実現したいことを、簡単に実装できる
先ほどの例にもありました。
「このクラスがコントローラだとプログラムに示したい!」⇒「@Controller」を付与して終わり
など。一目瞭然ですね。
2.簡潔で分かりやすく、誰がコードを書いても同じになる
これは最初に用いた@NotNull(必須項目とするためのアノテーション)で考えると分かりやすいです。
もしこれをアノテーションを使わずに実装するとどうなるか。
例えばこんな感じで実装できるかもしれません。(だいぶはしょってます)
そう。実装方法は1つではないのです。
しかし、実装者のやり方によってコードの書き方が異なると、チームなど複数人で開発をしているときに統一性が取れず、コードの可読性が落ちてしまいます。
では、アノテーションを使うとどうか。
@NotNullアノテーションは@NotNullアノテーションなので、実装者によって書き方が変わることはありません。見た目的にも分かりやすいですし、統一性が取れますね。
4.アノテーション付与のための設定方法
いよいよ、アノテーションの付与方法について見て行きます。
ここまでさんざん「@~さえつければ…」と書いてきてアレなんですが、
アノテーションを使うために、実は2つの作業が必要です。
作業1 インポート宣言
一番最初に@NotNullを用いてアノテーションを説明したとき、
実は上のほうにこんな記述が隠れていたのにお気づきでしょうか。
import javax.validation.constraints.NotNull;
アノテーションはSpringが用意してくれているものですが、「このクラスでこのアノテーションを使えるようにして」を毎回宣言してあげないといけないんです。(この宣言をインポート宣言といいます)
でもやり方は意外と簡単。
使いたいアノテーションを書いた後、エラーを示す「×」マークと、アノテーションの下に赤の波線が出ているかと思います。
この状態で、アノテーションにカーソルをあわせると、次の図のように、黄色い長方形に、エラーの解決方法候補がいくつか出てきます。
おそらく1つ目に、「(付与したいアノテーションの名前)をインポートします 」
というような文言があるはずなので、それをクリック。
(出てこない場合(少なくとも初回は出ません)⇒作業2を先にやってください。)
すると、クラス宣言の上のところに、自動で宣言が追加されます。
またこの時、アノテーションについていたエラーマークと赤い波線は消えています。
作業2 依存ライブラリの追加
作業1をうまく機能させるために、前提としてやるべき作業を説明します。
"pom.xml"というファイル(Springプロジェクト作成時に、自動的に作成されています)に、設定を追加していきます。
このpom.xmlファイルを開くと、<dependencies></dependencies>タグでくくられた箇所があります。そこに設定を追記していきます。
追記する内容ですが、適切な記述をネットから探してきて、コピペすることになります。内容の調べ方は以下。
追加したいアノテーションの名称と、"Maven Repository"を打ち込んでググり、以下のようなページを見つけてください。
(例では、@NotNull と、Maven Repositoryを打ち込んでググってみました)
このページの真ん中あたりにある、Mavenでくくられた箇所にご注目ください。(図の赤枠の場所)
ここに書かれた内容をまるまる全部コピーします。
コピーしたものを、先ほどの、pom.xmlファイルの<dependencies></dependencies>タグでくくられた箇所に貼り付けます。
これでpom.xmlの設定作業は終わりです。(この設定作業を、「依存ライブラリの追加」といいます)
ただし、アノテーションは複数のグループ毎に分けられていて、そのグループ毎に同様の設定作業をする必要があります。(詳しくはまた後日、別の記事にて。ここではひとまず、作業1のインポート宣言がうまく行かない時、作業2をやってみれば良いんだと思ってもらえればOKです)
5.まとめ
アノテーションは、プログラムに作業してほしいことを簡潔に伝えるための有力な手段であり、
その付与を有効とするためには、pom.xmlでの設定を前提として、クラス内でのインポート宣言が必要であることを紹介しました。