昨年作ったJava EE6のWebプロトタイプをベースにArquillianでインテグレーションテストを実際に作っています。2月から本格的に開発に入るとのことで、駆け込みで色々ちゃんと確認しないと;
で、今日は半日くらい悩んだことがあって…でも結果的には自分の悲しいミスだったので自分への戒めとして残しておきます(-_-;
Could not create stateless EJB!
前にEJBを使ったサンプルを書いてみて問題なく動いていたので、安心してプロトにも組み込んでいったのですが、あるEJBを以下のようにInjectして、テストを実行するとエラーとなりました。
package jp.co.hoge.hogehoge.bean;
import java.io.File;
import java.util.List;
import javax.inject.Inject;
import jp.co.hoge.hogehoge.business.OpportunityBusinessLogic;
import static org.hamcrest.CoreMatchers.*;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian;
import org.jboss.shrinkwrap.api.Archive;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.junit.After;
import org.junit.AfterClass;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(Arquillian.class)
public class OpportunityListBeanTest {
public OpportunityListBeanTest() {
}
@Deployment
public static Archive createDeployment(){
return ShrinkWrap.create(WebArchive.class, "OpportunityListBeanTest.war").
addPackage("jp.co.hoge.hogehoge.bean").
addAsResource("META-INF/persistence.xml").
addAsWebInfResource(new File("src/test/resources", "glassfish-web.xml")).
addAsWebInfResource(new File("src/main/webapp", "WEB-INF/beans.xml"));
}
@BeforeClass
public static void setUpClass() {
}
@AfterClass
public static void tearDownClass() {
}
@Before
public void setUp() {
}
@After
public void tearDown() {
}
@Inject
OpportunityBusinessLogic opportunityBusinessLogic;
@Test
public void 商談検索_商談名で一本引きするパターンテスト(){
List selectList = opportunityBusinessLogic.searchOpportunityList("OpportunityName","商談6080", null, null, true, false, true, "makimaki");
assertThat(selectList.size(), is(1));
}
}
エラーは以下のもの。
javax.ejb.EJBException: javax.ejb.CreateException: Could not create stateless EJB

あれ、なんでこのプロトだとうまくできなんだろ?と。何か色々試してやったけど駄目で…。
今思えば、ここでググってしまったのが間違いだったですが、ググった所、同じエラーで悩む色々な情報があり、もしやstatelessなEJBでは使えない?、いや、そんなはずはない、とか思いつつ、色々なサイトにいっては試して…という繰り返しをしていました。
で、一度原点に帰って、以前に作ったシンプルなEJBでのサンプルを引きずり出し、実行すると動きます。絶対にプロトの中になにかあるー、と思ってログをしっかりみると

IdGenerator.javaという自分達で作ったソースファイルが!
最初にググってしまったことによる先入観と、長いStacktraceをちゃんと読んでいなかった自分に喝を入れねばなりません。
で、なぜここで落ちるか、さらに探ってみると、WEB-INF/classes配下にxmlファイルを定義しており、それを読む処理があって、このxmlがWebArchiveに入っていなかったというオチ…。
propertiesファイルなんかも気を付けないとハマりそうだなぁ、と思いました。
Arquillianはwarをデプロイして本格的な試験ができて良いですが、依存性の記述欠如やこうしたファイル群の記述欠如による失敗も多かったりして、慣れるまで注意が必要かなと。
修正後
classes配下にファイルを置くのも微妙な気がしなくもないのですが、こういった場合でも以下のようなコードでちゃんと食わせられました。
@Deployment
public static Archive createDeployment(){
return ShrinkWrap.create(WebArchive.class, "OpportunityListBeanTest.war").
addPackage("jp.co.hoge.hogehoge.bean").
addAsResource("META-INF/persistence.xml").
addAsDirectory("WEB-INF/classes").
addAsWebInfResource(new File("src/test/resources", "glassfish-web.xml")).
addAsWebInfResource(new File("src/main/webapp", "WEB-INF/beans.xml")).
addAsResource(new File("src/main/webapp/WEB-INF/classes" ,"idgenerator.xml"));
}
補足
と書いていたのですが、なぜか動かなくなりました…(-_-;これ書いたときは確認してちゃんとxmlが読まれるのを確認したのですが…何か変に見逃してしまったのか…。
以下標準出力にもあるようにファイルはArchiveにいるはずなんですが。
Archiveの中身の簡単な確認方法
以下はArquillianのGetting Startedにも載っているのですが、ShrinkWrapのcreateを使う所で以下のように記述しておけば、簡単にArchiveの中身が確認できます。多くのサンプルは直接returnする形式が多いですが、意外と便利なんじゃ…と思ったり(^^;
@Deployment
public static Archive createDeployment(){
Archive archive = ShrinkWrap.create(WebArchive.class, "OpportunityListBeanTest.war").
addPackage("jp.co.hoge.hogehoge.bean").
addAsResource("META-INF/persistence.xml").
addAsDirectory("WEB-INF/classes").
addAsWebInfResource(new File("src/test/resources", "glassfish-web.xml")).
addAsWebInfResource(new File("src/main/webapp", "WEB-INF/beans.xml")).
addAsResource(new File("src/main/webapp/WEB-INF/classes" ,"idgenerator.xml"));
System.out.println(archive.toString(true));
return archive;
}
}
以下のような出力がされます。
OpportunityListBeanTest.war:
/WEB-INF/
/WEB-INF/beans.xml
/WEB-INF/glassfish-web.xml
/WEB-INF/classes/
/WEB-INF/classes/jp/
/WEB-INF/classes/jp/co/
/WEB-INF/classes/jp/co/hoge/
/WEB-INF/classes/jp/co/hoge/hogehoge/
/WEB-INF/classes/jp/co/hoge/hogehoge/encryption/
/WEB-INF/classes/jp/co/hoge/hogehoge/encryption/Sha256.class
/WEB-INF/classes/jp/co/hoge/hogehoge/encryption/Md5.class
/WEB-INF/classes/jp/co/hoge/hogehoge/encryption/IMessageDigest.class
/WEB-INF/classes/jp/co/hoge/hogehoge/id/
/WEB-INF/classes/jp/co/hoge/hogehoge/id/RootId.class
/WEB-INF/classes/jp/co/hoge/hogehoge/id/AbstractRootId.class
/WEB-INF/classes/jp/co/hoge/hogehoge/id/AbstractIdGenerator.class
/WEB-INF/classes/jp/co/hoge/hogehoge/id/IdGenerator.class
~中略~
/WEB-INF/classes/idgenerator.xml
/WEB-INF/classes/META-INF/
/WEB-INF/classes/META-INF/persistence.xml
これがあれば、ちゃんとwarの中身を確認できるので安全かなと。
ふぅ、今日はテストコードのデバッグに時間を食ってしまった感じです…(-_-;