CLOVER🍀

That was when it all began.

Arquillian Remoteを使ったテスト

前回までで、ManagedモードでArquillianを使ったEJBとJAX-RSのテストをやってみたので、今回はRemoteモードを使ってみます。

今回の対象は、EJBとJAX-RSリソースとして作成したクラスとします。

依存関係の定義とテスト対象

依存関係の定義。
build.sbt

name := "arquillian-remote"

version := "0.0.1-SNAPSHOT"

scalaVersion := "2.10.3"

organization := "littlewings"

resolvers += "Public JBoss Group" at "http://repository.jboss.org/nexus/content/groups/public-jboss"

fork in Test := true

libraryDependencies ++= Seq(
  "org.jboss.spec" % "jboss-javaee-web-6.0" % "3.0.2.Final" % "provided",
  "org.jboss.as" % "jboss-as-arquillian-container-remote" % "7.1.1.Final" % "test" excludeAll(
    ExclusionRule(organization = "org.jboss.shrinkwrap"),
    ExclusionRule(organization = "org.jboss.shrinkwrap.descriptors")
  ),
  "org.jboss.arquillian.junit" % "arquillian-junit-container" % "1.1.2.Final" % "test" excludeAll(
    ExclusionRule(organization = "org.jboss.shrinkwrap"),
    ExclusionRule(organization = "org.jboss.shrinkwrap.descriptors")
  ),
  "org.jboss.arquillian.protocol" % "arquillian-protocol-servlet" % "1.1.2.Final" % "test",
  "org.jboss.shrinkwrap" % "shrinkwrap-api" % "1.0.0-cr-1" % "test",
  "org.jboss.shrinkwrap" % "shrinkwrap-impl-base" % "1.1.2" % "test",
  "org.jboss.shrinkwrap.descriptors" % "shrinkwrap-descriptors-spi" % "2.0.0-alpha-3" % "test",
  "org.jboss.shrinkwrap.resolver" % "shrinkwrap-resolver-depchain" % "2.0.1" % "test" excludeAll(
    ExclusionRule(organization = "org.jboss.shrinkwrap")
  ),
  "org.jboss.resteasy" % "resteasy-jaxrs" % "2.3.2.Final" % "test",
  "junit" % "junit" % "4.11" % "test",
  "com.novocode" % "junit-interface" % "0.10" % "test"
)

sbtを使った場合のShrinkWrapの依存関係での注意点は、Managedモードと同じでした。

また、テスト時のプロトコルはServletとしています。

  "org.jboss.arquillian.protocol" % "arquillian-protocol-servlet" % "1.1.2.Final" % "test",

テスト対象となるクラスは、簡単なものにしておきます。まずはEJB。
src/main/scala/javaee6/web/service/CalcService.scala

package javaee6.web.service

import javax.ejb.{LocalBean, Stateless}

@Stateless
@LocalBean
class CalcService {
  def add(left: Int, right: Int): Int =
    left + right

  def multiply(left: Int, right: Int): Int =
    left * right
}

JAX-RSの有効化。
src/main/scala/javaee6/web/rest/JaxrsApplication.scala

package javaee6.web.rest

import javax.ws.rs.ApplicationPath
import javax.ws.rs.core.Application

@ApplicationPath("/rest")
class JaxrsApplication extends Application

Scalaを使った場合、メソッドとか定義しないんなら、波括弧って要らないの忘れてました…。

JAX-RSリソースクラス。
src/main/scala/javaee6/web/rest/CalcResource.scala

package javaee6.web.rest

import javax.enterprise.context.RequestScoped
import javax.ws.rs.{GET, Path, Produces, QueryParam}
import javax.ws.rs.core.MediaType

@RequestScoped
@Path("calc")
class CalcResource {
  @GET
  @Path("add")
  @Produces(Array(MediaType.TEXT_PLAIN))
  def add(@QueryParam("p1") p1: Int, @QueryParam("p2") p2: Int): String =
    (p1 + p2).toString
}

CDIも、一応有効化しておきます。

src/main/webapp/WEB-INF/beans.xml 

テストコード

Arquillianの設定。デフォルトプロトコルを、Servlet 3.0に。
src/test/resources/arquillian.xml

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

  <defaultProtocol type="Servlet 3.0"/>

</arquillian>

Maven使ってないから、profileの切り替え方がわかりません!今回は困りませんけど。

EJBのテスト。
src/test/scala/javaee6/web/service/CalcServiceTest.scala

package javaee6.web.service

import javax.ejb.EJB
import javax.inject.Inject

import org.jboss.arquillian.container.test.api.Deployment
import org.jboss.arquillian.junit.Arquillian
import org.jboss.shrinkwrap.api.ShrinkWrap
import org.jboss.shrinkwrap.api.asset.EmptyAsset
import org.jboss.shrinkwrap.api.spec.WebArchive
import org.jboss.shrinkwrap.resolver.api.maven.Maven

import org.junit.Test
import org.junit.runner.RunWith

import org.junit.Assert._
import org.hamcrest.CoreMatchers._

@RunWith(classOf[Arquillian])
class CalcServiceTest {
  // CDIを使わず、LocalBeanとする場合はJNDI名が必要らしい
  //@EJB(mappedName = "java:global/arquillian-test/CalcService")
  @Inject
  private var calcService: CalcService = _

  @Test
  def addTest: Unit =
    assertThat(calcService.add(1, 2), is(3))

  @Test
  def multiplyTest: Unit =
    assertThat(calcService.multiply(2, 3), is(6))
}

object CalcServiceTest {
  @Deployment
  def createDeployment: WebArchive =
    ShrinkWrap
      .create(classOf[WebArchive], "arquillian-test.war")
      .addPackages(true, "javaee6.web.service")
      .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml")
      .addAsLibraries {
        Maven
          .resolver
          .resolve("org.scala-lang:scala-library:2.10.3")
          .withTransitivity
          .asFile: _*
      }
}

ちょっとハマったのは、ここでした。

  // CDIを使わず、LocalBeanとする場合はJNDI名が必要らしい
  //@EJB(mappedName = "java:global/arquillian-test/CalcService")
  @Inject
  private var calcService: CalcService = _

前回、ManagedモードでEJBのテストをした時は@Injectアノテーションでテストしていたのですが、軽い気持ちで@EJBアノテーションを試してみたら、見事にハマりました。

ローカルBeanを使う時は、JNDIルックアップ名を指定する必要があるみたいです…。

Explicite Local EJB not injected with Arquillian
http://stackoverflow.com/questions/10089957/explicite-local-ejb-not-injected-with-arquillian

もしくは、CDIを使う場合はbeans.xmlをデプロイするようにします。

      .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml")

続いて、JAX-RSのテスト。
src/test/scala/javaee6/web/rest/CalcResourceTest.scala

package javaee6.web.rest

import java.io.File
import java.net.URL

import javax.ws.rs.ApplicationPath
import javax.ws.rs.core.{MediaType, Response}

import org.jboss.arquillian.container.test.api.{Deployment, RunAsClient}
import org.jboss.arquillian.junit.Arquillian
import org.jboss.arquillian.test.api.ArquillianResource
import org.jboss.resteasy.client.ClientRequest
import org.jboss.shrinkwrap.api.ShrinkWrap
import org.jboss.shrinkwrap.api.asset.EmptyAsset
import org.jboss.shrinkwrap.api.spec.WebArchive
import org.jboss.shrinkwrap.resolver.api.maven.Maven

import org.junit.Test
import org.junit.runner.RunWith

import org.junit.Assert._
import org.hamcrest.CoreMatchers._

@RunWith(classOf[Arquillian])
@RunAsClient
class CalcResourceTest {
  private val resourcePrefix =
    classOf[JaxrsApplication]
      .getAnnotation(classOf[ApplicationPath])
      .value
      .substring(1)

  @ArquillianResource
  private var deploymentUrl: URL = _

  @Test
  def calcTest: Unit = {
    val request
      = new ClientRequest(s"${deploymentUrl}${resourcePrefix}/calc/add?p1=3&p2=4")
    request.header("Accept", MediaType.TEXT_PLAIN)
    val response = request.get(classOf[String])
    assertThat(response.getStatus, is(Response.Status.OK.getStatusCode))
    assertThat(response.getEntity, is("7"))
  }
}

object CalcResourceTest {
  @Deployment
  def createDeployment: WebArchive =
    ShrinkWrap
      .create(classOf[WebArchive], "arquillian-test.war")
      .addPackages(true, "javaee6.web.rest")
      .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml")
      .addAsLibraries {
        Maven
          .resolver
          .resolve("org.scala-lang:scala-library:2.10.3")
          .withTransitivity
          .asFile: _*
      }
}

実はいろいろハマったのですが、コピペミスとかそういうくだらないことでした…。

で、テストのやり方なのですが、どうやればいいのかがわからず、とりあえずJBoss ASを起動して

jboss-as-7.1.1.Final/bin/standalone.sh

テストを実行したら、普通に認識してくれました…。

> test

確かに、Managedモードでやるより高速ですね。

一応、JMXでも問題なく実行できました。

とりあえず、Arquillianの普通の使い方はひとめぐりしました?的な。

今回のソースは、こちらにアップしています。
https://github.com/kazuhira-r/javaee6-scala-examples/tree/master/arquillian-remote