ArquillianとScalaTestを合わせて使う、最初にArquillianを使った時に思っていたことですが、最初からそんなことするとハマりそうだと思ってやめていました。
で、それぞれを別個に使ってきて、合わせられそうな気がしたので、今回トライ。これで、個人的にはArquillianネタは一段落かと思っています。
依存関係の定義とテスト対象
まずは、ビルドの依存関係。
build.sbt
name := "arquillian-scalatest-integration" 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", "org.scalatest" %% "scalatest" % "2.0" % "test" )
1番下に、ScalaTestを入れています。また、Arquillianを使ったテストはRemoteモードとします。
続いて、テスト対象のコードを用意。
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/RestApplication.scala
package javaee6.web.rest import javax.ws.rs.ApplicationPath import javax.ws.rs.core.Application @ApplicationPath("/rest") class RestApplication extends Application
src/main/scala/javaee6/web/rest/CalcResource.scala
package javaee6.web.rest import javax.ws.rs.{GET, Path, Produces, QueryParam} import javax.ws.rs.core.MediaType @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 @GET @Path("multiply") @Produces(Array(MediaType.TEXT_PLAIN)) def multiply(@QueryParam("p1") p1: Int, @QueryParam("p2") p2: Int): String = (p1 * p2).toString }
いずれも、単純なクラスですね。
beans.xmlも作成。
src/main/webapp/WEB-INF/beans.xml
では、続いてテストコードに移りましょう。
ScalaTestとJUnitを合わせて使う
Arquillianは、JUnitのRunnerと@InjectなどのCDIに関する処理を行うため、ScalaTestを使うといっても@Testアノテーションを使ったJUnitベースの書き方をする必要があると考えています。なので、ScalaTestのJUnit統合クラスを使用。
Getting started with JUnit 4 and Scala
http://www.scalatest.org/getting_started_with_junit_4_in_scala
テストコード自体は、ScalaTestのMatcherを使いたいので、以下のクラスをテストクラスは継承することにします。
import org.scalatest.junit.JUnitSuite
参考)
一応、FunSuiteでもJUnit Runnerは使えるみたいなのですが
Using JUnitRunner
http://www.scalatest.org/user_guide/using_junit_runner
やっぱりこのスタイルだと、@Injectアノテーションがうまく動きませんでした。そりゃあ、そうですよね。
では、まずはEJBのテストコード。
src/test/scala/javaee6/web/service/CalcServiceTest.scala
package javaee6.web.service import java.io.File 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.scalatest.Matchers._ import org.scalatest.junit.JUnitSuite import org.junit.Test import org.junit.runner.RunWith @RunWith(classOf[Arquillian]) class CalcServiceTest extends JUnitSuite { @Inject private var calcService: CalcService = _ @Test def addTest: Unit = { calcService.add(1, 2) should be (3) calcService.add(5, 5) should be (10) } @Test def multiplyTest: Unit = { calcService.multiply(1, 2) should be (2) calcService.multiply(5, 5) should be (25) } } object CalcServiceTest { @Deployment def createDeployment: WebArchive = { val depLibs: (String*) => Seq[File] = artifacts => artifacts.map { artifact => Maven .resolver .resolve(artifact) .withTransitivity .asFile }.flatten ShrinkWrap .create(classOf[WebArchive], "arquillian-test.war") .addPackages(true, classOf[CalcService].getPackage) .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml") .addAsLibraries { depLibs("org.scala-lang:scala-library:2.10.3", "org.scalatest:scalatest_2.10:2.0"): _* } } }
ScalaTestをJUnitを使ったスタイルで書くので、JUnitSuiteクラスを継承。
@RunWith(classOf[Arquillian]) class CalcServiceTest extends JUnitSuite {
もちろん、RunnerにArquillianも指定して。
これで、JUnitの@Testアノテーションを使ってテストコードを書きます。
@Test
def addTest: Unit = {
値の検証自体は、ScalaTestのMatcherで行います。
calcService.add(1, 2) should be (3) calcService.add(5, 5) should be (10)
あと、余談ですが、デプロイするWARにはScalaTestも入れる必要があったみたいなので、Scala本体と合わせてこんな感じにしました。
@Deployment def createDeployment: WebArchive = { val depLibs: (String*) => Seq[File] = artifacts => artifacts.map { artifact => Maven .resolver .resolve(artifact) .withTransitivity .asFile }.flatten ShrinkWrap .create(classOf[WebArchive], "arquillian-test.war") .addPackages(true, classOf[CalcService].getPackage) .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml") .addAsLibraries { depLibs("org.scala-lang:scala-library:2.10.3", "org.scalatest:scalatest_2.10:2.0"): _* } }
続いて、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.scalatest.Matchers._ import org.scalatest.junit.JUnitSuite import org.junit.Test import org.junit.runner.RunWith @RunWith(classOf[Arquillian]) @RunAsClient class CalcResourceTest extends JUnitSuite { private val resourcePrefix: String = classOf[RestApplication] .getAnnotation(classOf[ApplicationPath]) .value .substring(1) @ArquillianResource private var deploymentUrl: URL = _ @Test def addTest: Unit = { val request = new ClientRequest(s"${deploymentUrl}${resourcePrefix}/calc/add?p1=1&p2=2") request.header("Accept", MediaType.TEXT_PLAIN) val response = request.get(classOf[String]) response.getStatus should be (Response.Status.OK.getStatusCode) response.getEntity should be ("3") } @Test def multiplyTest: Unit = { val request = new ClientRequest(s"${deploymentUrl}${resourcePrefix}/calc/multiply?p1=1&p2=2") request.header("Accept", MediaType.TEXT_PLAIN) val response = request.get(classOf[String]) response.getStatus should be (Response.Status.OK.getStatusCode) response.getEntity should be ("2") } } object CalcResourceTest { @Deployment def createDeployment: WebArchive = { val depLibs: (String*) => Seq[File] = artifacts => artifacts.map { artifact => Maven .resolver .resolve(artifact) .withTransitivity .asFile }.flatten ShrinkWrap .create(classOf[WebArchive], "arquillian-test.war") .addPackages(true, classOf[CalcResource].getPackage) .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml") .addAsLibraries { depLibs("org.scala-lang:scala-library:2.10.3", "org.scalatest:scalatest_2.10:2.0"): _* } } }
最後に、Arquillianのプロトコル設定を行って
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>
JBoss ASを起動して
jboss-as-7.1.1.Final/bin/standalone.sh
テスト〜。
> test
テストケース、4つパス。OKですね。
> test [info] CalcResourceTest: 12 08, 2013 11:07:53 午後 org.xnio.Xnio <clinit> INFO: XNIO Version 3.0.3.GA 12 08, 2013 11:07:53 午後 org.xnio.nio.NioXnio <clinit> INFO: XNIO NIO Implementation Version 3.0.3.GA 12 08, 2013 11:07:53 午後 org.jboss.remoting3.EndpointImpl <clinit> INFO: JBoss Remoting version 3.2.3.GA SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder". SLF4J: Defaulting to no-operation (NOP) logger implementation SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details. [info] - multiplyTest [info] - addTest [info] CalcServiceTest: [info] - multiplyTest [info] - addTest [info] Run completed in 26 seconds, 694 milliseconds. [info] Total number of tests run: 4 [info] Suites: completed 2, aborted 0 [info] Tests: succeeded 4, failed 0, canceled 0, ignored 0, pending 0 [info] All tests passed. [success] Total time: 27 s, completed 2013/12/08 23:08:15
こんな感じで。今回のコードは、こちらに置いておきました。
https://github.com/kazuhira-r/javaee6-scala-examples/tree/master/arquillian-scalatest-integration