在每次测试之前,我应该如何重置JPA控制的数据库?

时间:2010-09-10 05:35:26

标签: java hibernate testing jpa integration-testing

在单元测试之前,是否存在使用JPA将数据库完全重置为新铺设的架构的最佳实践模式?我一直在使用带有hbml2ddl.auto = create-or-drop的测试持久性单元,并在每次测试之前重新创建EMF,但我想知道是否有更简洁的方法来实现它。

4 个答案:

答案 0 :(得分:1)

单元测试不应与数据库通信。

假设您正在为数据访问层编写集成测试,您可以使用DBUnit之类的工具,或者您可以创建一个静态测试助手,通过执行所有删除操作以编程方式重置您的数据库状态。在事务内部使用JPA查询进行插入。

答案 1 :(得分:1)

如果使用快速Java数据库(如H2 database或HSQLDB),则重置数据库不是一个大问题。与使用Oracle / MySQL(或用于生产的任何东西)相比,这将加速您的测试,并且它将确保您使用“真实”生产数据库时测试所有代码。

为了获得最佳性能,您可以在内存中使用H2(这样您可能无需手动重置数据库 - 如果连接已关闭则会自动重置),或者您可以使用常规持久性数据库。要在H2中使用后重置数据库,请运行(native)语句'drop all objects delete files'。

答案 2 :(得分:1)

DBUnit拥有您需要的大部分内容,我使用Springs Testing框架进行回滚,每次测试后的事务都参见http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/testing.html

答案 3 :(得分:0)

  

在单元测试之前,是否存在使用JPA将数据库完全重置为新铺设的架构的最佳实践模式?

在每次单元测试之前不要重置整个数据库模式,而是在每次单元测试的结束时重置“数据库环境(特定于当前单元测试)”。

我们有一个实体......

@Entity
public class Candidate implements {
    private String id;
    private String userName;
    private EntityLifeCycle lifeCycle;

    protected Candidate() {
    }
    public Candidate(String userName) {
        this.userName = userName;
    }

    @Id @GeneratedValue(generator="uuid", strategy=GenerationType.AUTO)
    @GenericGenerator(name="uuid", strategy="uuid", parameters = {})
    @Column(name="candidate_id", nullable=false, unique=true)
    public String getId() {
        return id;
    }
    protected void setId(String id) {
        this.id = id;
    }

    @Column(name="user_name", nullable=false, unique=true)
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }

    @Embedded
    public EntityLifeCycle getLifeCycle() {
        if(lifeCycle == null) {
            lifeCycle = new EntityLifeCycleImpl();
        }
        return lifeCycle;
    }
    public void setLifeCycle(EntityLifeCycleImpl lifeCycle) {
        this.lifeCycle = lifeCycle;
    }

    @PrePersist
    public void prePersist() {
         lifeCycle.setCreatedDate(new Date());
    }
}

我们在prePersist()方法中为每个Candidate实例设置createdDate。这是一个测试用例,断言createdDate正确设置....

   public class EntityLifeCycleTest {
    @Test
    public void testLifeCycle() {
        EntityManager manager = entityManagerFactory.createEntityManager();
        Candidate bond = new Candidate("Miss. Bond");
        EntityTransaction tx = manager.getTransaction();
        tx.begin();
        manager.persist(bond);
        tx.commit();

        Assert.assertNotNull(bond.getLifeCycle().getCreatedDate());
        manager.close();
    }
}

此测试用例将首次正常运行。但是如果我们第二次运行这个测试用例,它会抛出ConstraintViolationException,因为userName是唯一键。

因此,我认为正确的方法是在每个测试用例结束时“清理数据库环境(特定于当前单元测试)”。像这样......

public class EntityLifeCycleTest extends JavaPersistenceTest {
    @Test
    public void testLifeCycle() {
        EntityManager manager = entityManagerFactory.createEntityManager();
        Candidate bond = new Candidate("Miss. Bond");
        EntityTransaction tx = manager.getTransaction();
        tx.begin();
        manager.persist(bond);
        tx.commit();

        Assert.assertNotNull(bond.getLifeCycle().getCreatedDate());

      /* delete Candidate bond, so next time we can run this test case successfully*/
        tx = manager.getTransaction();
        tx.begin();
        manager.remove(bond);
        tx.commit();
        manager.close();
    }
}
  

我一直在使用带有hbml2ddl.auto = create-or-drop的测试持久性单元,并在每次测试之前重新创建EMF,但我想知道是否有更简洁的方法。

在每次测试之前重新创建EMF非常耗时,IMO。

仅当您对影响底层数据库的@Entity注释类进行了一些更改(例如添加/删除列和/或约束)时,才删除并重新创建数据库模式。因此,首先验证模式,如果模式有效则不重新创建模式,如果模式无效则重新创建模式。像这样......

public class JavaPersistenceTest {
    protected static EntityManagerFactory entityManagerFactory;
@BeforeClass
public static void setUp() throws Exception {
    if(entityManagerFactory == null) {
         Map<String, String> properties = new HashMap<String, String>(1);
         try {
           properties.put("hibernate.hbm2ddl.auto", "validate");
          entityManagerFactory = Persistence.createEntityManagerFactory("default", properties);
    } catch (PersistenceException e) {
        e.printStackTrace();
        properties.put("hibernate.hbm2ddl.auto", "create");
        entityManagerFactory = Persistence.createEntityManagerFactory("default", properties);
    }
         }
}
}

现在,如果您一次性运行所有测试用例(扩展JavaPersistenceTest),EMF将只创建一次(如果架构无效,则创建两次)。