昨年作ったJava EE6のプロトタイプにArquillianを使ったテストを入れてどんなことまでできるか確認してます。
単体レベルの試験も簡単にできるし、DB使った試験も簡単だし、かなり良い感じです。
とりあえず今のところ、躓いたのはFacesContext周辺。
画面サイドはSeleniumを利用してブラウザレベルでグルグルテストするので、Arquillianで画面サイドまではテストするつもりはないのですが、ログインのbacking beanの中で、ログ出力のためにクライアントのIPアドレスを取得するような以下プログラムがあって
private String getRemoteIpAddr(){
ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
HttpServletRequest request = (HttpServletRequest)externalContext.getRequest();
return request.getRemoteAddr();
}
テストすると、FacesContext.getCurrentInstance()がnullとなってしまって失敗してしまいます。この辺はJSFUnitを使う、という記事がありましたが、今の所そこまで気合い入ってなくて、ここをちょいと抜けてくれればいいんだけど…という思いがありました。
元のコードを変えて、そもそもFacesContext.getCurrentInstance()がnullの場合は、みたいな分岐エラー処理を入れてもいいかと思ったのですが…うーん、どうしよう、と。
悩んでいる中「FacesContext null test」とかでググってたら、そもそもArquillian関係なく、JUnitでbacking beanをテストするケースがあって、同じように困っている人が多々。
で、対策の1つにモックを使う方法がありました。なるほど。
参考にさせて頂いたのは「JSF:mocking FacesContext for unit tests」の記事です。
記事にあるContextMockerクラスをお借りして、以下コードをお試ししたら、ちゃんとテストできました!
モック作りにMockitoを使うので、まずはpom.xmlに記述
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.9.5</version>
<scope>test</scope>
</dependency>
ContextMockerクラスはテストパッケージ内に作成しました。
ログインのbacking beanのログイン処理をテストします
ログイン成功時の戻り値は画面遷移先のURLなので、それを比較します。
@Test
public void ログイン成功のテスト(){
FacesContext context = ContextMocker.mockFacesContext();
ExternalContext ext = mock(ExternalContext.class);
HttpServletRequest request = mock(HttpServletRequest.class);
when(context.getExternalContext()).thenReturn(ext);
when(ext.getRequest()).thenReturn(request);
when(request.getRemoteAddr()).thenReturn("10.255.255.1");
loginBean.setUserId("horikita");
loginBean.setPassword("makimaki");
assertThat(loginBean.login(), is("/opportunity/opportunityList.xhtml"));
}
テストの結果
どーん。やったー!JSFUnitまで手を出すのはしんどいなぁと思っていたのですが、これなら簡単なので、良い感じです。すごいー。