JPAで作ったデータアクセスクラスのテストをJUnitで作る際に、EntityManagerの扱いが問題になってきます。 プロダクトコードだと、PersistenceUnitのtransactionTypeをJTAとするため、 この設定のままユニットテストを実行には、APサーバが起動している必要があります。 この状態だとJenkinsに食わせたりするときに色々大変だし、 そもそもテスト用にはデータベースを別にしたいので、設定をテスト時だけ書き直す必要が出てきて面倒です。 そこでプロダクトコードとテストコードで参照するPersistenceUnitを変更するようにしてみました。
ファイル構成
ファイルは下記のように、プロダクトコードとテストコード用にそれぞれpersistence.xmlを用意します。
root
└ src
├ main
│ ├ java
│ │ ├ Account.java
│ │ └ AccountFacade.java
│ └ resources
│ └ META-INF
│ └ persistence.xml
└ test
├ java
│ └ AccountFacadeTest.java
└ resources
└ META-INF
└ persistence.xml
プロダクトコードのpersistence.xmlはこんな感じ。 transaction-typeをJTAにしているので、jta-data-sourceにAPサーバで設定したデータソース名を指定しています。
persistence.xml:
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
<persistence-unit name="mytestPU" transaction-type="JTA">
<jta-data-source>jdbc/mytest</jta-data-source>
<properties>
<property name="eclipselink.ddl-generation" value="create-tables"/>
</properties>
</persistence-unit>
</persistence>
テストコードのpersistence.xmlはこんな感じ。 プロダクトコードとは違い、APサーバが起動していなくてもユニットテストが実行できるように transaction-typeをRESOURCE_LOCALにしています。
persistence.xml:
<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0">
<persistence-unit name="mytestTPU" transaction-type="RESOURCE_LOCAL">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<class>org.mytest.entity.Account</class>
<properties>
<property name="javax.persistence.jdbc.driver" value="org.postgresql.Driver"/>
<property name="javax.persistence.jdbc.url" value="jdbc:postgresql://localhost:5432/mytest_test"/>
<property name="javax.persistence.jdbc.user" value="demo"/>
<property name="javax.persistence.jdbc.password" value="demo"/>
<property name="eclipselink.ddl-generation" value="drop-and-create-tables"/>
</properties>
</persistence-unit>
</persistence>
あとは下記のコードを用意します。
Account.java:
@Entity
public class Account
{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long accountId;
private String email;
...
}
AccountFacade.java:
@RequestScoped
@Transactional
public class AccountFacade
{
@PersistentContext(unitName = "mytestPU")
private EntityManager em;
public void persist(Account account)
{
Objects.requireNonNull(account);
em.persist(account);
}
}
AccountFacadeTest.java:
public class AccountFacadeTest
{
private EntityManager em;
private AccountFacade accountFacade = new AccountFacade();
@Before
public void setUp() throws Exception
{
em = Persistence.createEntityManagerFactory("mytestTPU").createEntityManager();
accountFacade.em = em;
}
@After
public void tearDown() throws Exception
{
em.close();
}
@Test
public void testPersist() throws Exception
{
final Account account = new Account("xxxxx@hoge.com");
em.getTransaction().begin();
accountFacade.persist(account);
em.getTransaction().commit();
assert account.getAccountId() > 0;
}
}
テストコードではmytestTPUからEntityManagerを生成します。 プロダクトコードでは@Transactionalでトランザクションを管理していますが、 テストコードでは@Transactionalでのトランザクション管理はできないので、 トランザクション制御のためのコードを書く必要があります。
Best Regards.