JavaEEでもTDD - JPA編 - 1
JavaEEでUnitTest書く方法はモックを使うとかglassfish-embedded使うとか色々合ったわけですが、先日コンテナ非依存のテストツールであるArquillianがJBossより正式リリースしたので、試して見ることにしました。まずは、こないだ痛い目を見たのでJPA周りをTDDしてみます。開発環境はNetBeans 7.1.2, GlassFish 3.1.2ですが、Mavenベースにしてあるので、Eclipseでも問題なくそのまま動きました。
1. プロジェクトの作成
まずはプロジェクトの作成をします。「新規作成」 -> 「Maven」 -> 「Webアプリケーション」で作成します。
サーバはGlassFish 3.1.2を選択します。JBossとか別なのを使う場合は、後述のpom.xmlの設定を適当に変更してください。
プロジェクトの作成が終わったら、pom.xmlにArquillianはじめ必要なライブラリの設定等を追加していきます。長いのでブログには載せませんので、こちらを参照にしてください。
2. テスト対象の作成
次に、記事を表すEntityのArticleを作成します。普通にフルスクラッチしても良いのですが、面倒なのでNetBeansの「新規Entityの作成」である程度のひな形を作ります。これでArticleのひな形とpersistance.xmlが作成されたのが確認できると思います。
つづいて、Entityを操作するDAOクラスを作ります。「EntityクラスからセッションBean」を作成を選択して、利用Entityに先ほど作ったArticleを追加して完了します。DAOのArticleFacadeと抽象クラスのAbstractFacadeが作成されたのが確認できると思います。この辺が結構自動生成されるのがNetBeansの良い所です。
3. はじめてのテスト
では、早速、最初のテストを書いていきましょう。「新規JUnitテスト」でtest側の同一パッケージにArticleFacadeTestを作成します。
作成したテストクラスに下記の用にArquillian用の記述を追加します。
@RunWith(Arquillian.class) public class ArticleFacadeTest { @PersistenceContext EntityManager em; @Inject UserTransaction utx; public ArticleFacadeTest() { } @Deployment public static Archive createDeployment() { return ShrinkWrap.create(WebArchive.class, "test.war"). addPackage(Article.class.getPackage()). addPackage(ArticleFacade.class.getPackage()). addAsResource("META-INF/persistence.xml"). addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml"); } @EJB ArticleFacade articleFacade; }
@Deploymentの 部分でテスト用のコンテナが立ち上がります。addPackageでは追加したいクラスのパッケージを登録します。addAsResourceで指定しているpersistance.xmlは今回は先程生成されたものが参照しています。beans.xmlはまだ作成していないので、
src/main/resources/META-INF/beans.xml
に空ファイルを作成します。
次は、persistance.xmlがテストで参照するデータソースを指定するためにsrc/test/resources/arquillian.xmlを下記のように作成ます。
<?xml version="1.0" encoding="UTF-8"?> <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"> <engine> <property name="deploymentExportPath">target/arquillian</property> </engine> <container default="true" qualifier="glassfish"> <configuration> <property name="sunResourcesXml">src/main/setup/glassfish-resources.xml</property> </configuration> </container> </arquillian>
続いて、sunResourcesXmlで指定されているglassfish-resources.xmlを下記の通り作ります。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE resources PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Resource Definitions//EN" "http://glassfish.org/dtds/glassfish-resources_1_5.dtd"> <resources> <jdbc-connection-pool name="arquillianPool" res-type="javax.sql.DataSource" datasource-classname="org.apache.derby.jdbc.ClientDataSource" pool-resize-quantity="1" max-pool-size="5" steady-pool-size="0" statement-timeout-in-seconds="60" > <property name="serverName" value="localhost" /> <property name="portNumber" value="1527" /> <property name="dataBaseName" value="sun-appserv-samples" /> <property name="User" value="APP" /> <property name="Password" value="APP" /> <property name="connectionAttributes" value=";create=true" /> <property name="driverType" value="4" /> </jdbc-connection-pool> <jdbc-resource jndi-name="jdbc/arquillian" pool-name="arquillianPool" /< </resources>
これでテストの起動時に適切なDataSourceが作成されます。本番だとMySQLやOracleになると思いますが、TDDの最中は手軽なDerbyを指定して見ました。ここを変更すればMySQL等でも使えるので、結合テストや負荷テストで変えて確認するがの良いかと思います。この辺のDBを切り替えやすいのはJPAの恩恵ですね。
では、ようやく本命のテストコードです。まずは、データを何も登録してないので、レコードが空であることを確認するテストを書きます。
@Test public void count0_Test() throws Exception { assertThat(articleFacade.count(), is(0)); }
微妙にTDDの原則に反してますが、いきなりGreenになれば成功です。まあ、確認対象は自動生成コードなので、問題無いです。これが動けば、ひと通りの環境設定に誤りがないという事なので、あとはTDDを回していくだけです><
4. AbstractJPATestの作成
このまま進めても問題は無いのですが、後々の便利なので、ユーティリティメソッドとか共通フィールドを持った親クラスを作ります。内容はこんな感じ. 以降はこのコードを継承した前提で進めます。
思ったより長くなったので続きは次の記事で。なお、現在作成しているとこまではgithubのこちらのタグからcheckoutできます。