2015年7月2日木曜日

Spring で MockMVC を使ったテストで Filter を使用する方法

概要

MockMvc のテストで Servlet の Filter を使用する方法を紹介します
MockMvc を使ったテスト環境の構築はこちらの記事を参考にして準備してください
また、Spring + Filter の使い方に関してはこちらを御覧ください

環境

  • Mac OS X 10.10.3
  • Eclipse Luna 4.4.1
  • Spring Framework 4.1.6
  • Spring Tool Suite 3.6.4
  • Java 1.8.0_31
  • Maven 3.2.1

Filter オブジェクトの作成

テスト用のクラスファイルに以下を追記します
@Autowiredを使ってクラスの作成と同時に Filter オブジェクトを生成します

@Autowired
private TestFilter testFilter;

フィルタの名前は自身の環境に合わせて適宜変更してください
また必要に合わせてimport文も定義してください

Beans として登録する

src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xmlに以下を追記します
@Autowiredで定義したフィルタオブジェクトをBeansの仕組みを使って生成するためです

<!-- for Filter -->
<beans:bean id="testFilter" class="com.sample.TestFilter">
</beans:bean>

MockMvc にフィルタを登録する

作成したフィルタをMockMvcのテスト時に使用するようにフィルタを追加します
MockMvc を WebApplicationContext から生成しているところで以下のようにフィルタを追加してください

this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).addFilter(this.testFilter, "/").build();

addFilterメソッドを呼び出します
第一引数でフィルタを指定して第二引数でフィルタを適用するURLマッピングを指定します

動作確認

mvn package等でテストを実行してフィルタの動作が実行されているか確認してください

最後に

web.xmlでアプリ的にフィルタを使うように定義していてもMockMvcでテストした際に有効にならないことに気が付き今回の対応を実施しました
web.xmlで定義していればテストでも通るようにしていいと思うんだけど、こうなっているのは MockMvc が Servlet の世界とは別世界のクラスだからなのだろうか
それとも親切心からなのだろうか

2015年7月1日水曜日

Spring で MockMVC を使ってテストしてみる

概要

Spring Framework には End to End の のテストを実現するためにMockMvcという仕組みが存在しています
簡単にいうとAPIをコールしてその結果を評価するという一連の流れをテストすることができます

環境

  • Mac OS X 10.10.3
  • Eclipse Luna 4.4.1
  • Spring Framework 4.1.6
  • Spring Tool Suite 3.6.4
  • Java 1.8.0_31
  • Maven 3.2.1

サンプルプロジェクト作成

過去の記事を参考に作成してください
Spring Framework のバージョンが最新でない場合は pom.xml 「org.springframework-version」のバージョン記載部分を最新のバージョンに変更してください

Spring-Test のライブラリを追加する

pom.xmlspring-testのライブラリを追加します

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-test</artifactId>
   <version>${org.springframework-version}</version>
</dependency>
<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>javax.servlet-api</artifactId>
  <version>3.1.0</version>
</dependency>

junitを定義するところが既にある場合でもバージョンが4.9未満である場合は4.9以上にしてください
javax.servletを読み込んでいますが、デフォルトだと読み込んでいるサーブレットのバージョンが2.5でこれだとCaused by: java.lang.ClassNotFoundException: javax.servlet.SessionCookieConfigと言われてテストが失敗するので 3.0 系のバージョンを読み込みます

テストコードの作成

src/test/java/test/sample/MockMvcSampleTest.javaにテストコードを作成していきます
STSでプロジェクトを作成するとテスト用のコードは何も作成されないので、適当にテスト用のクラスファイルを作成しましょう
作成したらMockMVCを使うための定義を記載します

package com.sample;

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"file:src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml"})
@WebAppConfiguration
public class MockMvcSampleTest {

    @Autowired
    private WebApplicationContext wac;

    protected MockMvc mockMvc;

    @Before
    public void setup() {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
    }

    @Test
    public void sampleTest() throws Exception {
        this.mockMvc.perform(get("/")).andExpect(status().isOk());
    }
}

いろいろ書いているので簡単にポイント説明します

クラスの定義の冒頭で3つのアノテーションを宣言しています

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"file:src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml"})
@WebAppConfiguration

JUnit + MockMvc を使ってテストするために必要な記述です
詳細は正直自分もよくわかっていませんが

  • RunWithでランナーを選択して
  • ContextConfigurationでサーブレットの設定が記述されたコンテキストファイルを読み込んで
  • WebAppConfigurationでWebアプリをテストするための必要なコンテストに触れるようにしています

とにかく1つでも定義がかけるとエラーになるので必ず定義します

次にMockMvcのオブジェクトを定義する部分です

@Autowired
private WebApplicationContext wac;

protected MockMvc mockMvc;

MockMvcのオブジェクトを作成するのにWebApplicationContextが必要で、このオブジェクトは先程のアノテーション+@Autowiredすることで勝手に設定されるオブジェクトです
この2つのオブジェクトを使って、サンプルのsetupメソッドにあるようにbuildするとMockMvcのオブジェクトが生成されます

最後にテストの方法です

this.mockMvc.perform(get("/")).andExpect(status().isOk());

ポイントはgetstatusというメソッドをimport文でstaticに読み込んでいる部分です
こうすることでテストを書く際にメソッドの呼び出しを直感的に行えます
performでコールするAPIのパスとHTTPメソッドを指定して、その結果をandExpectで評価する感じです
今回のテストの場合はレスポンスコードが20X系であることを確認するテストになっています

Tips

他のHTTPメソッドでコールしたい場合は

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;

こんな感じでimportして

this.mockMvc.perform(post("/")).andExpect(status().isMethodNotAllowed());

こんな感じでテストできます

リクエストヘッダを指定したい場合はperformの後に.header("key", "value")で指定できます

リクエストパラメータを指定したい場合はperformの後に.param("key", "value")で指定できます

(POSTやPUTの場合に)リクエストボディを設定したい場合はperformの後に.content("body_value")で指定できます

実行

STSで作成していれば自動的にMavenプロジェクトになっているのでmvn packageなどで実行すればライフサイクルの中にテストが含まれるので自動でテストされます
テストだけ個別で実行したい場合はmvn test-compile testなどとすれば実行されます

最後に

Spring Framework にはデフォルトで end to end のテスト方法が含まれているのでAPIサーバを作る場合には割りと簡単にテストが書けそうです
MockMvc を使わないテストも記述することもできるので、メソッド個別でテストしたい場合には別のテストメソッドを作成してテストするといいと思います
JUnitが使えるのでJUnitAssert等も使うことができます

次回はテスト時に Filter を通してテストする方法を紹介したいと思います

2015年6月30日火曜日

Spring で Filter を使う方法

概要

Spring Framework で Java のサーブレットの Filter を使ってみました

環境

  • Mac OS X 10.10.3
  • Eclipse Luna 4.4.1
  • Spring Framework 4.1.6
  • Spring Tool Suite 3.6.4
  • Java 1.8.0_31
  • Maven 3.2.1

サンプルプロジェクト作成

過去の記事を参考に作成してください
Spring Framework のバージョンが最新でない場合は pom.xml 「org.springframework-version」のバージョン記載部分を最新のバージョンに変更してください

Filterクラスの実行

今回は以下にクラスを作成することにします
src/main/java/com/sample/TestFilter.java
Javaのサーブレットのjavax.servlet.Filterを implements したクラスを作成します
今回は単純に doFilter 内でデバッグログを出力するようにします

package com.sample;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class TestFilter implements Filter {

  public TestFilter() {
  }

  @Override
  public void destroy() {
  }

  @Override
  public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
    System.out.println("start");
    chain.doFilter(req, res);
    System.out.println("end");
  }

  @Override
  public void init(FilterConfig arg0) throws ServletException {
  }
}

Filterの有効化

Filterを有効にするにはweb.xmlを修正します
STSでプロジェクトを作成した場合にはsrc/main/webapp/WEB-INF/web.xmlに以下を追記します

<filter>
  <filter-name>testFilter</filter-name>
  <filter-class>com.sample.TestFilter</filter-class>
</filter>
  <filter-mapping>
  <filter-name>testFilter</filter-name>
  <url-pattern>/</url-pattern>
</filter-mapping>

<web-app>配下に記載すればOKです

動作確認

プロジェクトを右クリックして「Run on Server」で起動しましょう
ブラウザで「http://localhost:8080/sample」とかにアクセスしてみましょう
com.sample.HomeControllerに定義してある @RequestMapping が実行する想定です

コンソールに以下のように表示されれば Filter が有効になっています

start
INFO : com.sample.sample.HomeController - Welcome home! The client locale is ja_JP.
end

start - end が Filter で出力しているデバッグ文でメイン処理の前に start が出力され、メイン処理の後に end が出力されていることがわかると思います

最後に

前に Spring で Interceptor を使う方法を紹介しましたが、Interceptor と Filter の使い分けとしては

  • Interceptor はあるメソッドの前後で実行される処理を定義して
  • Filter はある RequestMapping の前後で実行される処理を定義する

のが違いかなと思っています
粒度的には Interceptor のほうが細かい感じかなと思います
(違っていたらすいません、突っ込みお願いします)

2015年6月26日金曜日

/sbin/mount.vboxsf: mounting failed with the error: Protocol error

概要

Vagrant で共有ディレクトリをマウントしようとして

環境

  • Windows7 64bit
  • Vagrant 1.6.5
  • VirtualBox 4.3.18

対応方法

vagrant sshしてVM上で以下のコマンドを実行して共有ディレクトリをマウントします

mount -t vboxsf -o uid=`id -u vagrant`,gid=`getent group vagrant | cut -d: -f3` /vagrant /vagrant

これでもエラーが表示される場合は「-o」を指定しないで

mount -t vboxsf vagrant /vagrant/

で自分は何とかマウントできました
コマンドはrootユーザで実行しています

他の要因としてVBoxGuestAdditionがインストールされていない、ホストオンリーアダプタがついていなくてホストマシンと通信できない等がありますが、それをクリアした前提でコマンドを実行しています

2015年6月25日木曜日

Maven で Cobertura を使ってみた

概要

Maven でテストコードのカバレッジを表示することができる Cobertura を使ってみました

環境

  • Mac OS X 10.10.3
  • Eclipse Luna 4.4
  • Java 1.8.0_31
  • Maven 3.2.1

pom.xml に定義する

以下を定義します
<build>-><plugins>配下に記載します

<!-- cobertura -->
<plugin>
  <groupId>org.codehaus.mojo</groupId>
  <artifactId>cobertura-maven-plugin</artifactId>
  <version>2.7</version>
  <configuration>
    <formats>
      <format>html</format>
      <format>xml</format>
    </formats>
    <instrumentation>
      <excludes>
        <exclude>**/*Test.class</exclude>
      </excludes>
    </instrumentation>
    <executions>
      <execution>
        <phase>test</phase>
        <goals>
          <goal>check</goal>
        </goals>
      </execution>
    </executions>
  </configuration>
</plugin>

記載したら実行しましょう
コマンドはmvn cobertura:coberturaです
成功するとtarget/cobertura/index.htmlが作成されるのでこれを開くとカバレッジ率を見ることができます

JavaはJREではなくJDKが必要になるので注意してください

最後に

mvn packageのライフサイクルに入れるにはどうすればいいのだろうか

2015年6月24日水曜日

Maven で Javadoc を生成する方法

概要

Maven で Javadoc を作成してみました

環境

  • Mac OS X 10.10.3
  • Eclipse Luna 4.4
  • Java 1.8.0_31
  • Maven 3.2.1

プロジェクト作成

プロジェクトは Eclipse 上で作成しました
以下の Group Id と Artifact Id を指定しています

  • Group Id・・・org.apache.maven.archetypes
  • Artifact Id・・・maven-archetype-quickstart

Javadoc 出力設定

上記 Artifact Id で作成すれば特に何もしなくても Javadoc を生成することができます
以下の goal を指定すれば作成できます

mvn javadoc:javadoc

上記で実行するとtarget/site/apidoc/index.htmlに Javadoc の index.html が生成されます
2015/06/15 現在だと使用されるmaven-javadoc-pluginのバージョンは2.10.3でした

pom.xml に定義する場合

例えば出力先や文字コードを変更したい場合はpom.xmlに記載する必要があります
<project>-><build>-><plugins>配下に以下を記載してください

<!-- Javadoc -->
<plugin>
   <groupId>org.apache.maven.plugins</groupId>
   <artifactId>maven-javadoc-plugin</artifactId>
   <version>2.10.3</version>
   <configuration>
      <author>true</author>
      <source>1.8</source>
      <show>protected</show>
      <encoding>UTF-8</encoding>
      <charset>UTF-8</charset>
      <docencoding>UTF-8</docencoding>
   </configuration>
</plugin>

<version>に使用するプラグインのバージョンを指定します
<encoding>にJavaのソースファイルのエンコードを指定します
<charset>に出力するHTMLの文字コードを指定します
<docencoding><charset>と同じ(だと思います)

他の詳しいパラメータはここを参考にしてください

実行する場合は先ほど同様でmvn javadoc:javadocでOKです

最後に

mvn packageのライフサイクルに入れるにはどうすればいいのだろうか

2015年6月23日火曜日

disabled by the php.ini setting phar.readonly

概要

Phing でビルド時にタイトルのエラーが出現したので、その対処方法を紹介します

環境

  • CentOS 6.5 64bit ( on Vagrant )
  • PHP 5.3.3
  • Phing 2.7.0

対処方法

php.ini を直接書き換えます

  • vi /etc/php.ini
; phar.readonly = On

これを以下のように変更します

phar.readonly = false

これで再度phing buildを実行してみたください