Author Archives: S.TK

2017-03-13

Mockでユニットテストを簡単にしよう!

はじめに

こんにちは。NIKKOエンジニアのS.TKです。

皆さん、テストはしていますか?最近の開発手法であれば、ほぼ確実にテストが考慮されているので嫌でもしていますよね。ただ、テストって実は結構難しかったりします。特にテストコードを書くとなると、プロダクトコードの設計によってはかなり苦労させられます。

そこで、今回はユニットテスト(単体テスト)に焦点を当て、テストコードを楽に書くためにMock(モック)を利用する方法をご紹介します。私はGMO MARS DMPの開発・運用を担当していますが、今回ご紹介する内容は普段の業務で実践している内容になります。
言語はJavaで、テストフレームワークはJUnitを使うことにします。

ユニットテストを書こう

まず最初に、ユニットテストを書くことの意義について再確認してみたいと思います。今更感がすごいですが、ちょっとだけお付き合いください。

一番期待されるのは、やはり品質の向上でしょうか。誤りを早い段階で発見できますし、既存機能を変更する際に誤りを防止することができます。また、プロダクトコードをリファクタリングする際には、ユニットテストが誤り混入の防波堤の役割を果たしますので、機能面だけでなくコード面の品質も向上させることができます。

他にも、テストコードの書き方を工夫するとプロダクトコードの使い方を明示することができます。つまり、テスト対象クラスの使い方サンプルとしてテストコードを利用することができます。テストコードは実際に動作可能なので、ドキュメントよりも信頼性が高いはずです(テストコードが「古い」場合はテストがfailする可能性が高いため)。

さらに進めて考えると、設計の品質向上にも貢献します。ユニットテストの容易さで設計のマズさがある程度わかるためです。これについては、本稿で実感頂けるかと思います。

ユニットテストでよく発生する問題

さて、ユニットテストの大切さを再確認したところで、実際にテストコードを書いてみると……これが案外難しいものです。テスト対象クラスに外部リソースにアクセスする処理や実行環境に依存する処理があり、自分のローカル環境でテストが動かせない……というのはありがちです。

たとえば、下記のようなidからユーザ名を取得するような簡単なクラスで考えてみます。ユーザの情報はDBに格納されているため、DBに接続してデータを取得する処理が必要になります。

このクラスのユニットテストを実行することを考えると、自分のローカル環境にDBをつくるかDB接続の設定を行う必要があります。また、実際にユニットテストを実行させると、DBとの接続やテストデータ投入でかなりの時間がかかってしまいます。

Mockを使おう

このような場合、Mockを使うと上手く解決ができます。

Mockとは、簡単に言うとクラスの動作をシミュレートするためのオブジェクトです。テスト対象クラスが呼び出している(=依存している)クラスをMockで差し替え、Mockの動作内容を定義することで、望むテスト条件を容易に作ることができます。

Mockを扱うライブラリは各言語に色々存在していますが、Java言語で有名所ですとMockitoというライブラリがあります。Mockitoは1系と2系がありますが、今回は1系を扱います。

実際、Mockitoはどんな感じで使うのかというと、下記のようになります。

プロダクトコードがこちら。

テストコードがこちら。

テストコードの8行目がMockオブジェクトを作成している箇所で、11行目でそのMockの動作を規定しています。この例ですと、「isSomething()メソッドが引数100で呼ばれた時にtrueを返す」ことを定めています。そして、このテストケースではisSomething()がtrueを返してきた場合にテスト対象メソッドが正しく-1を返していることをテストしています。

このように、Mockを使うとテスト対象クラスが依存しているクラスの動作をシミュレートし、テストケースの事前条件を容易に整えることが可能となります。

Mockを実際に使ってみる

では、前述のSomeServiceのテストコードをMockを使って書いてみます。

早速、依存しているクラスをMockに差し替えてやりましょう!……依存クラスがないですね。これではMockに差し替えられません。

このようなケースは特にレガシーコードではよくあることです。以下で順を追ってプロダクトコードを修正し、Mockで差し替えられる設計にしましょう。

責務外の処理は別クラスに移譲しよう

まずDBにアクセスしている部分はSomeServiceの責務とは外れているので、別クラスに移譲してしまいましょう。たとえば、UserMapperクラスとでもしてしまいましょう。

実際に修正すると下記のようになります。

これで面倒なDBアクセス部分は別クラスになりましたので、簡単にテストコードが書け……ませんね。これだとUserMapperをインスタンス化した際に結局DBアクセスが発生してしまいますし、Mockで差し替えることもできません。

依存するオブジェクトは外部から注入できるようにしよう

では次に、UserMapperオブジェクトをインスタンス化する部分をクラス外に持って行ってしまいましょう。つまり、SomeServiceを使う側がUserMapperをインスタンス化し、それをSomeServiceに「注入」してあげることにします。注入方法はコンストラクタで良いでしょう。

やっとできました。これでDBアクセス部分をMockで差し替えることができます。

Mockでユニットテストを簡単に書こう

修正後のSomeServiceで、Mockを使って実際にテストコードを書いてみます。この場合はUserMapperが依存しているクラスなので、このクラスのMockを注入してあげればOKですね。具体的なテストコードは下記のようになります。

DBアクセスを担当するUserMapperクラスをMockにしているため、DB接続ができない環境でも問題なく動作します。これで自分のローカル環境でもガンガンテストを動かせますね!

このように、Mockを使ってあげると、余計な処理を省いて本当にテストしたい内容だけをテストコードとして書くことができます。

ユニットテストと設計の関係

前述のような依存するインスタンスを外部から注入する設計は、一般にDI(Dependency Injection)として知られています。DIコンテナも多数存在しており、有名どころではSpringなどがあげられます。
DIできるように設計しておくと、ユニットテストでMockを容易に注入できるため、テストコードがとても書きやすくなります。逆に、テストコードが書きにくいと感じたら、プロダクトコードの設計がマズイ可能性が高いです。ユニットテストによって設計の品質が向上するというのはまさにこの点を指しています。

おわりに

本稿では、ユニットテストを簡単にするためにMockを使う方法を紹介してきました。途中から設計の話も混ざってきましたが、これはユニットテストがある意味では設計作業でもあるためです。ユニットテストの容易さとクラスの使い勝手の良さは大抵の場合イコールになるものです。
この例ではJava言語を使っていますが、大抵の言語でこの考え方は通用します。皆さんも、プロダクトコードを書く時に「このコードのテストはどうやって書けば良いだろうか」という点を意識してみるのは如何でしょうか。

2016-10-17

EditorConfigでEclilpseのテンプレートとオサラバしよう!

こんにちは。NIKKOエンジニアのS.TKです。今回は簡単に導入できるエディタ設定の共有ツールを紹介したいと思います。

皆さんは開発の際にインデントをタブ、スペースのどちらにするか、文字エンコーディングを何にするか等のルールで揉めた経験はありませんか?また、ルールを決めても結局守られなかった……といった経験はありませんか?

今回紹介する EditorConfig は、こういう問題に対する解決策の一つです。

EditorConfigとは

EditorConfig は異なるエディタ、 IDE 間で編集ルールを統一することができるツールです(公式ページ)。

多くのエディタと IDE が対応しており、設定ファイルを置くだけで有効になるため、導入はとても簡単です。有名どころでは以下のエディタ、 IDE が対応しています(詳細は公式ページを参照)。

  • Notepad++
  • Atom
  • Emacs
  • Vim
  • Eclipse
  • NetBeans
  • IntelliJ IDEA

導入方法

ファイルと同じ階層に設定ファイル( .editorconfig )を置けば導入完了です。エディタまたは IDE が EditorConfig に対応していることを確認してください。

.editorconfig ファイルはこんな感じに書きます。

EditorConfig の設定ファイル

設定ファイルは .editorconfig という名前の INI 形式ファイルです。  [ と ] でセクションを指定し、 name = value でプロパティを設定します。また、コメントは # か ; で開始します。

設定の優先度

結論から言うと、開いたファイルに近い .editorconfig の設定値が優先されます。具体的な優先度は以下のようにして決められます。

  1. 開いたファイルと同階層から上位の階層へと順番に .editorconfig を探索する。
  2. 最上位階層に到達するか、rootプロパティにtrueが指定されている .editorconfig を発見したなら探索を止める。
  3. 見つかった .editorconfig を上位階層から順番に適用する。よって同じプロパティに異なる値が設定されている場合、開いたファイルに一番近い .editorconfig の設定値が優先される。

セクションの形式

セクションにはルールを適用するファイルをワイルドカード等を使用して指定します。実際に使用できるパターンは以下のとおりです。
なお、特殊文字を文字として使いたい場合はバックスラッシュでエスケープしてください。

パターン 説明
* パス区切り文字( / )以外の任意の文字列にマッチ。
** パス区切り文字( / )も含めた任意の文字列にマッチ。
? 任意の1文字にマッチ。
[name] nameに含まれている任意の1文字にマッチ。
[!name] nameに含まれていない任意の1文字にマッチ。
{s1,s2,s3} カンマ区切りで与えられた文字列のいずれかにマッチ。
{num1..num2} num1からnum2までの範囲の整数値にマッチ。

プロパティの一覧

プロパティにはファイルに適用するルールを指定します。実際に使用できるプロパティは以下のとおりです。
なお、プロパティを設定しなかった場合は、エディタまたはIDEの設定がそのまま使われます。

プロパティ名 設定可能な値 説明
indent_style tab
space
インデントの形式を指定する。
tabはタブ、spaceはスペースを使用する。
indent_size 数値
tab
インデント時のスペースの個数を指定する。
tabを指定した場合、tab_widthプロパティの指定値を適用する。
tab_width 数値 タブを表現するためのスペースの個数を指定する。
デフォルト値としてindent_sizeプロパティの値が指定されているので、通常は設定する必要なし。
end_of_line lf
cr
crlf
改行コードの形式を指定する。
charset latin1
utf-8
utf-16be
utf-16le
文字エンコーディングを指定する。
trim_trailing_whitespace true
false
trueの場合、行末の空白文字を削除する。
insert_final_newline true
false
trueの場合、ファイル保存時にファイル末尾が改行記号でなければ改行記号を付与する。
root true trueの場合、このファイルより上位の階層の .editorconfig ファイルを探索しない。

まとめ

今回は、エディタ設定を共有するツールとしてEditorConfigを紹介しました。対応しているエディタ、IDEであれば設定ファイルを置くだけで導入できますので、是非試してみてください。