読者です 読者をやめる 読者になる 読者になる

Challenge Java EE !

Java EEを中心に趣味や仕事における開発メモを書いています。

Arquillianテストで自分のミスに気付かずハマってたこと

EJB Arquillian

昨年作った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

f:id:kikutaro777:20130125201543j:plain

あれ、なんでこのプロトだとうまくできなんだろ?と。何か色々試してやったけど駄目で…。

今思えば、ここでググってしまったのが間違いだったですが、ググった所、同じエラーで悩む色々な情報があり、もしやstatelessなEJBでは使えない?、いや、そんなはずはない、とか思いつつ、色々なサイトにいっては試して…という繰り返しをしていました。

で、一度原点に帰って、以前に作ったシンプルなEJBでのサンプルを引きずり出し、実行すると動きます。絶対にプロトの中になにかあるー、と思ってログをしっかりみると

f:id:kikutaro777:20130125200735j:plain

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"));
        //Archiveの中身を標準出力
        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の中身を確認できるので安全かなと。
ふぅ、今日はテストコードのデバッグに時間を食ってしまった感じです…(-_-;

にほんブログ村 IT技術ブログへ
にほんブログ村
にほんブログ村 IT技術ブログ Javaへ
にほんブログ村