PlayFramework2.1.3(Java)+JPA 使ってみる #3
UnitTestできるようになったので、UnitTestを書いてみる。DDLはJPAにやらせるので、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ディレクトリ下なんだっけ?