LoginSignup
27
24

More than 5 years have passed since last update.

JPA & JAX-RS のテストを Arquillian を使ってやってみた(テストカバレッジ取得のおまけつき)

Last updated at Posted at 2015-02-26

Java EE でテストやるなら Arquillian

Oracle の強力なプッシュもあってか、Java EE が勢いを盛り返しつつある昨今ですが、Java EE にはテスティングフレームワークはありません。やっぱり JUnit とか使います。

しかし、Java EE はアプリケーションサーバーによるコンテナ管理を中心に動作するので、単純に new でオブジェクトを生成して試すことはできず、JUnit だけでは力不足。そこで登場するのが Arquillian

arquillian_logo_450px.png

Arquillian · Write Real Tests

Arquillian is an innovative and highly extensible testing platform for the JVM that enables developers to easily create automated integration, functional and acceptance tests for Java middleware.

Arquillian は JBoss Community で開発されている Java EE 結合テスト実行プラットフォーム。これ使えばモックなしでテストができるらしい。

試行環境

  • Windows 8.1
  • WildFly 8.2
  • Lombok
  • H2 Database 1.3
  • Eclipse Luna
  • Gradle 2.1

ソースコード

割と最小構成で作った。

ビルドスクリプト

  • テストを行うので javax:javaee-api じゃなくて、実装の付いてる org.jboss.spec:jboss-javaee-7.0 を指定。
build.gradle
apply plugin: 'war'

def javaVersion = 1.8
def defaultEncoding = 'UTF-8'

sourceCompatibility = javaVersion
targetCompatibility = javaVersion
tasks.withType(AbstractCompile) each { it.options.encoding = defaultEncoding }

webAppDirName = 'WebContent'

repositories {
  mavenCentral()
}

dependencies {
  providedCompile 'org.projectlombok:lombok:1.16.2'
  providedCompile 'org.jboss.spec:jboss-javaee-7.0:1.0.2.Final'
}

JPA

クラス

  • Lombok 使ってます。
Country.java
@SuppressWarnings("serial")
@Data
@Entity
@NamedQuery(name = Country.JPQL_SELECE_ALL, query = "SELECT c FROM Country c")
public class Country implements Serializable {

  public static final String JPQL_SELECE_ALL = "Country.selectALL";

  @Id @GeneratedValue
  private long id;

  private String name;

  private int population;

}
CountryRepository.java
@RequestScoped
public class CountryRepository {

  @PersistenceContext
  private EntityManager em;

  @Transactional
  public List<Country> getAllCountries() {
    return em.createNamedQuery(Country.JPQL_SELECE_ALL, Country.class).getResultList();
  }

}

設定ファイル

  • ファイルを使って WildFly にデータソースを登録。
WEB-INF/h2-ds.xml
<?xml version="1.0" encoding="UTF-8"?>
<datasources xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://www.ironjacamar.org/doc/schema"
    xsi:schemaLocation="http://www.ironjacamar.org/doc/schema http://www.ironjacamar.org/doc/schema/datasources_1_1.xsd">
    <datasource jndi-name="java:global/jdbc/example" pool-name="ExampleDS">
        <connection-url>jdbc:h2:mem:example;DB_CLOSE_DELAY=-1</connection-url>
        <driver>h2</driver>
    </datasource>
</datasources>
META-INF/persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1"
    xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">

    <persistence-unit name="sample-web-project">
        <jta-data-source>java:global/jdbc/example</jta-data-source>
        <class>entity.Country</class>
        <properties>
            <property name="javax.persistence.database-product-name"
                value="H2" />
            <property name="javax.persistence.database-major-version"
                value="1" />
            <property name="javax.persistence.database-minor-version"
                value="3" />
        </properties>
    </persistence-unit>

</persistence>

JAX-RS

ApplicationConfig.java
@ApplicationPath("api")
public class ApplicationConfig extends Application {
}
CountryResource.java
@Path("country")
@RequestScoped
public class CountryResource {

  @Inject
  private CountryRepository repository;

  @GET
  @Produces(MediaType.APPLICATION_JSON + ";charset=UTF-8")
  @Transactional
  public List<Country> getAllCountries() {
    return repository.getAllCountries();
  }

}

テストコード

ビルドスクリプト

  • Arquillian の依存性を取得するために、JBoss の Maven リポジトリを追加する。
  • jboss-javaee-7.0 は providedCompile で指定されており、@RunAsClient が付与されアプリケーションサーバー上で実行されない JAX-RS のテストコードは依存性不足に陥ってしまうため、testRuntime として別途依存性を追加しておく。
build.gradle
...

repositories {
  maven { url 'http://repository.jboss.org/nexus/content/groups/public' }
}

dependencies {
  testCompile 'junit:junit:4.12'
  testCompile 'org.jboss.arquillian.junit:arquillian-junit-container:1.1.7.Final'
  testCompile 'org.wildfly:wildfly-arquillian-container-remote:8.2.0.Final'

  testRuntime 'org.jboss.resteasy:resteasy-client:3.0.10.Final'
  testRuntime 'org.jboss.resteasy:resteasy-jackson-provider:3.0.10.Final'
}

JPA

設定ファイル

  • テスト用には別個のデータソース登録ファイルを作成した。
test-h2-ds.xml
<?xml version="1.0" encoding="UTF-8"?>
<datasources xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://www.ironjacamar.org/doc/schema"
    xsi:schemaLocation="http://www.ironjacamar.org/doc/schema http://www.ironjacamar.org/doc/schema/datasources_1_1.xsd">
    <datasource jndi-name="java:global/jdbc/exampleTest" pool-name="ExampleDS">
        <connection-url>jdbc:h2:mem:example-test;DB_CLOSE_DELAY=-1</connection-url>
        <driver>h2</driver>
    </datasource>
</datasources>
  • persistence.xml も別個に用意。
  • AP サーバー起動時に自動的にスキーマを作成し、データ登録スクリプトを実行する。
test-persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1"
    xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">

    <persistence-unit name="sample-web-project">
        <jta-data-source>java:global/jdbc/exampleTest</jta-data-source>
        <class>entity.Country</class>
        <properties>
            <property name="javax.persistence.schema-generation.database.action"
                value="drop-and-create" />
            <property name="javax.persistence.schema-generation.create-source"
                value="metadata" />
            <property name="javax.persistence.schema-generation.drop-source"
                value="metadata" />
            <property name="javax.persistence.sql-load-script-source"
                value="insert-initial-data.sql" />

            <property name="javax.persistence.database-product-name"
                value="H2" />
            <property name="javax.persistence.database-major-version"
                value="1" />
            <property name="javax.persistence.database-minor-version"
                value="3" />
        </properties>
    </persistence-unit>

</persistence>

insert-initial-data.sql
INSERT INTO Country SELECT * FROM CSVREAD('classpath:countries.csv', null, 'charset=UTF-8');
COMMIT;
countries.csv
id,name,population
1,Japan,127300000
2,USA,318900000
3,China,1357000000

テストクラス

  • 別個に用意した設定ファイルを WAR に含める。
CountryRepositoryTest.java
@RunWith(Arquillian.class)
public class CountryRepositoryTest {

  @Deployment
  public static WebArchive createDeployment() {
    return ShrinkWrap.create(WebArchive.class)
        .addClasses(Country.class, CountryRepository.class)
        .addAsResource("test-persistence.xml", "META-INF/persistence.xml")
        .addAsResource("insert-initial-data.sql")
        .addAsResource("countries.csv")
        .addAsWebInfResource("test-h2-ds.xml");
  }

  @Inject
  private CountryRepository repository;

  @Test
  public void getAll() {
    assertThat(repository.getAll().size(), is(3));
  }

}

JAX-RS

  • JPA のテスト同様、別個に用意した設定ファイルを WAR に含める。
  • @ArquillianResourceアノテーションでアプリケーションコンテキストを含む URL を取得できる。
  • Web API へのアクセスは、AP サーバー外で実行したいので@RunAsClientを付与する。
CountryResourceTest.java
@RunWith(Arquillian.class)
public class CountryResourceTest {

  @ArquillianResource
  private URL baseUrl;

  private String resourcePrefix = ApplicationConfig.class.getAnnotation(ApplicationPath.class)
      .value();

  @Deployment
  public static WebArchive createDeployment() {
    return ShrinkWrap.create(WebArchive.class)
        .addClasses(Country.class, CountryRepository.class, ApplicationConfig.class,
            CountryResource.class)
        .addAsResource("test-persistence.xml", "META-INF/persistence.xml")
        .addAsResource("insert-initial-data.sql")
        .addAsResource("countries.csv")
        .addAsWebInfResource("test-h2-ds.xml");
  }

  @Test
  @RunAsClient
  public void test() throws Exception {
    List<Country> list = ClientBuilder.newClient()
        .target(new URL(baseUrl, resourcePrefix + "/country").toURI())
        .request(MediaType.APPLICATION_JSON)
        .get(new GenericType<List<Country>>() {});

    assertThat(list.size(), is(3));
  }

}

Arquillian 設定ファイル

arquillian.xml
<?xml version="1.0"?>
<arquillian xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://jboss.org/schema/arquillian"
    xsi:schemaLocation="http://jboss.org/schema/arquillian http://www.jboss.org/schema/arquillian/arquillian_1_0.xsd">

    <defaultProtocol type="Servlet 3.0" />
    <container qualifier="arquillian-wildfly-remote" default="true" />

</arquillian>

テストカバレッジ取得

ビルドスクリプト

  • AP サーバー上でのカバレッジを取得するために arquillian-jacoco を依存性として追加。
  • arquillian-jacoco には org.jacoco.core が必要となるのでこれも追加。
build.gradle
...

def jacocoVersion = '0.7.2.201409121644'

dependencies {
  testCompile 'org.jboss.arquillian.extension:arquillian-jacoco:1.0.0.Alpha7'
  testCompile "org.jacoco:org.jacoco.core:${jacocoVersion}"
}

jacoco.toolVersion = jacocoVersion

テスト実行

gradle test jacocoTestReportを実行。

テスト
tests.PNG

テストカバレッジ
jacoco.PNG

できた!

でも、残念ながら@RunAsClientで実行した JAX-RS のテストは AP サーバー外で実行されているのでカバレッジが取得できていない模様。対処策の情報を探したけれど見つからなかった。無念。

感じたこと

  • ビルドツール使わない限りは Arquillian を使うのはちょっと厳しい。使い方覚えたほうがいい。
  • Java の諸ツール、Gradle への対応が進んでいるけれど、まだまだ Maven が中心になっていることが多い。
    • 特に Arquillian は Maven の使用が前提になっているなあと感じた。
    • Arquillian の Gradle プラグインもあるんだけど、あんまりメンテされてなくて WildFly に対応していない…。

参考サイト

27
24
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
27
24