在单元测试之前,是否存在使用JPA将数据库完全重置为新铺设的架构的最佳实践模式?我一直在使用带有hbml2ddl.auto = create-or-drop的测试持久性单元,并在每次测试之前重新创建EMF,但我想知道是否有更简洁的方法来实现它。
答案 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将只创建一次(如果架构无效,则创建两次)。