2014-04-08
■[Java] GitHub + Travis CI + CoverallsでJavaプログラムのカバレッジ計測をする方法
Travis CIとCoverallsを使って、GitHubに公開しているJavaプログラムのカバレッジを測定する方法を以下に記載する(Mavenで管理されたJavaプロジェクトを想定)。
Travis CIとCoverallsの連携にはCoverallsの公式ページ(以下)で紹介されているcoveralls-maven-pluginを利用した。
ステップ1(事前準備)
GitHubとTravis CIの連携は以下のサイト等を参考に実施しておく。
また、Coverallsのサイトにログインして、カバレッジ計測対象のGitHubリポジトリを有効化しておく。
ステップ2(pom.xmlの編集)
<plugin> <groupId>org.eluder.coveralls</groupId> <artifactId>coveralls-maven-plugin</artifactId> <version>2.2.0</version> </plugin>
GitHubのcoveralls-maven-pluginのREADMEを見ると、CoverallsのRepo Tokenも書いているが、GitHubの公開リポジトリを利用している場合には、記載してはダメのようだ。
次にpom.xmlに以下のような記載を追加することで、利用するカバレッジ測定ツールを指定する(以下はCoberturaの例)。JaCoCo等を使う場合にはcoveralls-maven-pluginのREAMMEを参照。
<plugin> <groupId>org.codehaus.mojo</groupId> <artifactId>cobertura-maven-plugin</artifactId> <version>2.6</version> <configuration> <format>xml</format> <maxmem>256m</maxmem> <aggregate>true</aggregate> </configuration> </plugin>
ステップ3(.travis.ymlの編集)
GitHubとTravis CI連携用に.travis.ymlファイルを作成していると思うが、その.travis.ymlに以下を追加する。
after_success: - mvn clean cobertura:cobertura coveralls:cobertura
以上で準備は完了。
GitHubにソースコードをpushすると、自動的にTravis CIでビルドとテストが走り、その後テストデータがCoverallsに自動転送され、テストの統計データを見られるようになる。
CoverallsのプロジェクトページのTECHNICAL DETAILSにバッジが表示されているが、以下のようにカバレッジ率が表示されていれば(unknownじゃなくなっていれば)連携成功である。
2014-03-04
■[JAVA] MockitoのArgumentMatcherの使い方
Error 404 (Not Found)!!1を見ると、このクラスを使って以下のように、IsListOfTwoElementsクラスのような独自のArgument Matcherを作る事が出来る。
class IsListOfTwoElements extends ArgumentMatcher<List> { public boolean matches(Object list) { return ((List) list).size() == 2; } } List mock = mock(List.class); when(mock.addAll(argThat(new IsListOfTwoElements()))).thenReturn(true); mock.addAll(Arrays.asList("one", "two")); verify(mock).addAll(argThat(new IsListOfTwoElements()));
また、argThat(new IsListOfTwoElements())の所は、以下のようにメソッドにすると、可読性が高くなると書かれている。
verify(mock).addAll(argThat(new IsListOfTwoElements())); //becomes verify(mock).addAll(listOfTwoElements());
メソッドにするやり方は書かれていないが、以下のようにすれば良い。
class IsListOfTwoElements extends ArgumentMatcher<List> { public boolean matches(Object list) { return ((List) list).size() == 2; } public static List listOfTwoElements() { return argThat(new IsListOfTwoElements()); } }
ちなみにジェネリックを使うと以下のような感じか。
class IsListOfTwoElements extends ArgumentMatcher<List<String>> { @Override public boolean matches(Object list) { return ((List<?>) list).size() == 2; } public static List<String> listOfTwoElements() { return argThat(new IsListOfTwoElements()); } } @SuppressWarnings("unchecked") List<String> mock = mock(List.class); when(mock.addAll(argThat(new IsListOfTwoElements()))).thenReturn(true); mock.addAll(Arrays.asList("one", "two")); verify(mock).addAll(listOfTwoElements());
2014-02-24
■[JAVA] System.out.printとかSystem.exitとかのユニットテストを補助してくれるルールを試してみる
System.out.printlnとかSystem.exitをテストするのはちょっと面倒なのだが、その辺りを補助してくれるJUnitのルールのコレクション(System Rules)があったので試してみた。
【事前準備】
System Rulesを使うにはApache Commons IOが必要という事でダウンロードしておく。
【ユニットテストの例】
テスト対象クラスは以下のように標準出力や標準エラー出力に文字列を出力するメソッドやSystem.exit(0)を呼び出すメソッドがある。
public class MyClass { public void out() { System.out.print("Hoge"); } public void err() { System.err.print("Moge"); } public void exit() { System.exit(0); } }
上記のテスト対象クラスをSystem Rulesを使ってテストするコードは以下のようになる。かなりシンプルにテストコードが書けた。
public class MyClassTest { @Rule public final StandardOutputStreamLog stdout = new StandardOutputStreamLog(); @Rule public final StandardErrorStreamLog stderr = new StandardErrorStreamLog(); @Rule public final ExpectedSystemExit exit = ExpectedSystemExit.none(); MyClass sut; @Before public void setUp() throws Exception { sut = new MyClass(); } @Test public void testOut() { sut.out(); assertThat(stdout.getLog(), is("Hoge")); } @Test public void testErr() { sut.err(); assertThat(stderr.getLog(), is("Moge")); } @Test public void testExit() { exit.expectSystemExitWithStatus(0); sut.exit(); } }
System Rulesは上記以外にも、システムプロパティのテストを補助するProvideSystemPropertyやセキュリティーマネージャのテストを補助するProvideSecurityManagerといったルールがあるようだ。
【関連記事】
2014-02-20
■[JAVA] MockitoのMatchers.anyObject()の使い方
MockitoのMatchers.anyObject()は以下のmethodA()のように、あるオブジェクトを引数に持つメソッドが呼び出されたかどうかを、Mockitoで検証する際等に使うことが出来る。
public class MyClass { private MyField field; public void setField(MyField field) { this.field = field; } public void methodA(String text) { field.methodB(new MyObject(text)); } }
具体的な検証コードは以下。
public class MyClassTest { MyField mock; MyClass sut; @Before public void setUp() throws Exception { mock = mock(MyField.class); sut = new MyClass(); sut.setField(mock); } @Test public void testMethodA() { sut.methodA("test"); // methodA()を呼び出すとその先でMyFieldのmethodB()が呼び出される事を検証 verify(mock).methodB((MyObject)anyObject()); } }
なお、検証コードを以下のようにしても良さそうなのだが、実際にmethodB()が呼び出された際に渡されたオブジェクトと同一のインスタンスでは無いため、以下はエラーとなってしまう。このような場合にMatchers.anyObject()が使える。
verify(mock).methodB(new MyObject("test"));
2014-02-19
■[JAVA] Mockitoでprivateなフィールドをモック化する方法
以下のようにprivateなフィールドをモック化する場合、リフレクションを使うと出来るのだが、Mockitoにはそのリフレクションを簡単に使えるWhiteboxというユーティリティクラスがある。
public class MyClass { private final MyField field = new MyField(); public void methodA() { field.methodB(); } }
Whiteboxクラスの使い方は以下(MyFieldクラスをモック化し、メソッドが呼ばれている事を検証する例)。
public class MyClassTest { MyClass sut; @Before public void setUp() throws Exception { sut = new MyClass(); Whitebox.setInternalState(sut, "field", mock(MyField.class)); } @Test public void testMethodA() { sut.methodA(); verify((MyField)Whitebox.getInternalState(sut, "field")).methodB(); } }