Seasar DI Container with AOP

S2EJB3UnitはS2Unitを拡張したテスティングフレームワークです。 S2EJB3UnitはEJB 3.0のコンポーネントとJava Persistence API(JPA)のエンティティに対するテストを効率化します。 S2EJB3Unitには次の特長があります。

  • ejb3.diconとhibernate-jpa.diconを自動的にincludeします。
  • registerメソッドによりEJB 3.0コンポーネントを組立てることができます。
  • Rollbackアノテーションが指定されたテストメソッドの実行前にトランザクションを開始し実行後にトランザクションをロールバックします。
  • EntityManagerを返すgetEntityManagerを提供します。
  • assertEntityEqualsメソッドもしくはassertEntityListEqualsメソッドによりExcelの期待値データとJPAのエンティティを比較します。

Example

EJB 3.0を使ったテスト

このサンプルでは、従業員の名前を返すServiceのステートレスセッションBeanをテストします。 シナリオとして従業員番号9900で検索をかけると従業員番号9900の従業員名を返すということを想定します。 ServiceのステートレスセッションBeanはDAOのステートレスセッションBeanを呼び出し、DAOのステートレスセッションBeanはデータベースにアクセスします。 データベースのアクセスにはJDBC APIを利用します。 このケースをテストするためには、検索のための従業員テーブルのデータが必要です。 データはExcelで用意します。データの作り方などについては適宜S2Unitのサンプルを参照してください。

ServiceとDAOそれぞれにビジネスインタフェースとステートレスセッションBeanを用意します。

examples.s2ejb3unit.EmployeeService

package examples.s2ejb3unit;

public interface EmployeeService {

    String getEmployeeName(int empno);
}

examples.s2ejb3unit.EmployeeServiceImpl

package examples.s2ejb3unit;

import javax.ejb.EJB;
import javax.ejb.Stateless;

@Stateless
public class EmployeeServiceImpl implements EmployeeService {

    @EJB
    private EmployeeDao dao;

    public String getEmployeeName(int empno) {
        return dao.getEmployeeName(empno);
    }

}

examples.s2ejb3unit.EmployeeDao

package examples.s2ejb3unit;

public interface EmployeeDao {

    String getEmployeeName(int empno);
}

examples.s2ejb3unit.EmployeeDaoImpl

package examples.s2ejb3unit;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import javax.annotation.Resource;
import javax.ejb.Stateless;
import javax.sql.DataSource;

@Stateless
public class EmployeeDaoImpl implements EmployeeDao {

    @Resource
    DataSource ds;

    public String getEmployeeName(int empno) {
        String ename = null;
        try {
            Connection con = ds.getConnection();
            try {
                PreparedStatement ps = con
                        .prepareStatement("SELECT ename FROM emp WHERE empno = ?");
                try {
                    ps.setInt(1, empno);
                    ResultSet rs = ps.executeQuery();
                    try {
                        if (rs.next()) {
                            ename = rs.getString("ename");
                        }
                    } finally {
                        rs.close();
                    }
                } finally {
                    ps.close();
                }
            } finally {
                con.close();
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
        return ename;
    }
}

examples.s2ejb3unit.EmployeeServiceImplTest

package examples.s2ejb3unit;

import javax.ejb.EJB;

import org.seasar.framework.ejb.unit.S2EJB3TestCase;
import org.seasar.framework.unit.annotation.Rollback;

public class EmployeeServiceImplTest extends S2EJB3TestCase {

    @EJB
    private EmployeeService service;

    @Override
    public void setUp() throws Exception {
        super.setUp();
        register(EmployeeDaoImpl.class);
        register(EmployeeServiceImpl.class);
    }

    @Rollback
    public void testGetEmployee() throws Exception {
        readXlsWriteDb("EmployeeServiceImplTest_getEmployeeName.xls");
        assertEquals("1", "SCOTT2", service.getEmployeeName(9900));
    }

}

テストクラスはS2EJB3TestCaseを継承します。 setUpメソッド内でregisterメソッドを呼ぶことでEJB 3.0のコンポーネントをコンテナに登録できます(setUpメソッドでは必ずsuper.setUp()を実行してください)。まとめて複数登録する場合や、コンポーネントの組み立て方が複雑な場合はdiconファイルを使い明示的にincludeします。 EJBアノテーションが指定されたフィールドには自動的にEJBのコンポーネントが設定されます。

テストメソッド実行前にトランザクションを開始し、実行後にトランザクションをロールバックしたい場合はテストメソッドにRollbackアノテーションを指定します。

Java Persistence APIを使ったテスト

このサンプルでは、従業員エンティティをテストします。 それぞれのテストメソッドでは事前データをデータベースに書き込み、その後エンティティを取得し期待値と比較しています。
public class EmployeeTest extends S2EJB3TestCase {

    @Rollback
    public void testFindEmployee() {
        readXlsWriteDb("Prepare.xls");
        DataSet expected = readXls("findEmployeeResult.xls");
        Employee actual = getEntityManager().find(Employee.class, new Long(1));
        assertEntityEquals("0", expected, actual);
    }

    @Rollback
    public void testFindEmployeeAndRelationship() {
        readXlsWriteDb("Prepare.xls");
        DataSet expected = readXls("findEmployeeRelationshipsResult.xls");
        Employee actual = getEntityManager().find(Employee.class, new Long(1));
        assertEntityEquals("0", expected, actual, true);
    }

    @Rollback
    public void testSelectAllEmployees() {
        readXlsWriteDb("Prepare.xls");
        DataSet expected = readXls("selectAllEmployeesResult.xls");
        List actual = getEntityManager().createQuery(
                "select e From Employee e").getResultList();
        assertEntityListEquals("0", expected, actual);
    }

    @Rollback
    public void testSelectAllEmployeesAndRelationships() {
        readXlsWriteDb("Prepare.xls");
        DataSet expected = readXls("selectAllEmployeesRelationshipsResult.xls");
        List actual = getEntityManager().createQuery(
                "select e From Employee e").getResultList();
        assertEntityListEquals("0", expected, actual, true);
    }
}

getEntityManagerメソッドを使いEntityManagerにアクセスしています。 EntityManagerを取得するにはhibernate-jpa.diconが読み込まれている必要があります。 hibernate-jpa.diconはS2Hibernate-JPAの設定ファイルです。 S2EJB3Unitは現在S2Hibernate-JPAとの連携のみをサポートしています。

S2EJB3TestCaseにはエンティティを期待値のDataSetと比較するためのアサートメソッドが4つ用意されています。

  1. assertEntityEquals(String message, DataSet expected, Object entity)
  2. assertEntityEquals(String message, DataSet expected, Object entity, boolean includesRelationships)
  3. assertEntityListEquals(String message, DataSet expected, Object entity)
  4. assertEntityListEquals(String message, DataSet expected, Object entity, boolean includesRelationships)

assertEntityEqualsメソッドは単一のエンティティとDataSetを比較します。 assertEntityListEqualsメソッドはエンティティのリストとDataSetを比較します。 assertEntityEqualsメソッドとassertEntityListEqualsメソッドはそれぞれ4つの引数をもつオーバーロードされたメソッドを持ちます。 4番目の引数であるincludesRelationshipsは、比較対象にエンティティのリレーションシップを含めるかどうかのbooleanです。 たとえば従業員エンティティが部門エンティティとリレーションシップをもちassertEntityEqualsメソッドの引数に従業員エンティティが渡されるとき、includesRelationshipsがfalseならば部門エンティティは比較対象になりません。trueのときは比較対象になります。 3つの引数をもつassertEntityEqualsメソッドとassertEntityListEqualsメソッドを利用する場合、includesRelationshipsはfalseだとみなされます。