PlayFramework2.1.3(Java)+JPA 使ってみる #3

play2.1(Java)でJPAを使ってみたメモの続き。

UnitTestできるようになったので、UnitTestを書いてみる。DDLJPAにやらせるので、evolution はパスで。

フィスクチャ注入

play1の play.test.Fixtures みたいのは Ebeanにはある。junit 上で投入してないけど。サンプル中のフィクスチャ投入コードがこれ。

Global.java - samples/java/zentasks

public class Global extends GlobalSettings {
    public void onStart(Application app) {
        InitialData.insert(app);
    }

    static class InitialData {
        public static void insert(Application app) {
            if(Ebean.find(User.class).findRowCount() == 0) {

                Map<String,List<Object>> all = (Map<String,List<Object>>)Yaml.load("initial-data.yml");

                // Insert users first
                Ebean.save(all.get("users"));

                // Insert projects
                Ebean.save(all.get("projects"));
                for(Object project: all.get("projects")) {
                    // Insert the project/user relation
                    Ebean.saveManyToManyAssociations(project, "members");
                }

                // Insert tasks
                Ebean.save(all.get("tasks"));
            }
        }
    }
}

おっと Global.java は GlobalSettings を継承して onStart フックし初期データを注入してる。play1 の @OnApplicationStart はこうなるのか。GlobalSettings のオーバーライドは ここ(インターセプター)で説明されてる。
h2:mem を使っているうちは、データが揮発してしまうので、onStart で注入するのもありだな。

JPAでは、サンプルのようにEBeanのフィスクチャ注入ができないので、EntityManager.createNativeQuery でやってみる。

とりあえずテスト書く

public class ModelTest
{
    @Test
    public void findByIdTest()
    {
        running(fakeApplication(), new Runnable() {
            public void run() {
                JPA.withTransaction(new play.libs.F.Callback0() {
                    public void invoke() {

                        String sql1 = "delete from company";
                        JPA.em().createNativeQuery(sql1).executeUpdate();
                        String sql2 = "insert into company (id,name) values (1,'Apple Inc.')";
                        JPA.em().createNativeQuery(sql2).executeUpdate();

                        Company apple = Company.findById(1L);

                        assertThat(apple).isNotNull();
                        assertThat(apple.name).isEqualTo("Apple Inc.");

                    }
                });
            }
         });
    }
}

これはひどい。

書き直し

ここを参考に書き直し。
SQLクエリ生成を EntityManager.createNativeQuery から DB.getConnection().createStatement() にしてみた。
DB.getConnection().createStatement() だとパラメータを渡せない(?)から使えないけど、フィクスチャ投入くらいになら使えるね。

package models;

import static org.fest.assertions.Assertions.*;

import java.util.Arrays;
import java.util.List;

import org.junit.Test;

public class ModelTest extends utils.UnitTest
{
    @Override
    public List<String> getFixtureSource()
    {
        return Arrays.asList(
                "delete from company",
                "insert into company (id,name) values (1,'Apple Inc.')"
                );
    }

    @Test
    public void findByIdTest()
    {
        Company apple = Company.findById(1L);

        assertThat(apple).isNotNull();
        assertThat(apple.name).isEqualTo("Apple Inc.");
    }
}
package utils;

import java.sql.SQLException;
import java.util.Collections;
import java.util.List;

import org.junit.After;
import org.junit.Before;

import play.Logger;
import play.db.DB;
import play.db.jpa.JPA;
import play.db.jpa.JPAPlugin;
import play.test.FakeApplication;
import play.test.Helpers;
import scala.Option;

public abstract class UnitTest
{
    protected FakeApplication app;
    protected javax.persistence.EntityManager em;

    @Before
    public void setUp()
    {
        FakeApplication app = Helpers.fakeApplication();
        Helpers.start(app);

        Option<JPAPlugin> jpaPlugin = app.getWrappedApplication().plugin(JPAPlugin.class);
        em = jpaPlugin.get().em("default");
        JPA.bindForCurrentThread(em);
        em.getTransaction().begin();

        loadFixtures();
    }

    @After
    public void tearDown()
    {
        em.getTransaction().commit();
        em.close();
        JPA.bindForCurrentThread(null);
        Helpers.stop(app);
    }

    protected void loadFixtures()
    {
        for (String sql : getFixtureSource()) {
            execSql(sql);
        }
    }

    protected List<String> getFixtureSource()
    {
        return Collections.emptyList();
    }

    protected void execSql(String sql)
    {
        try {
            DB.getConnection().createStatement().execute(sql);
        } catch (SQLException e) {
            Logger.error("execsql failed:" + sql, e);
        }
    }
}

ましになった。fakeApplication とか JPA.withTransaction とか書かなきゃいけないのは面倒だな。
Fixture の置き場所どうしようか。confディレクトリ下なんだっけ?