使用 DataIntegrityViolationException junit,spring,@Transactional 测试删除是否正确回滚
Testing that delete is correctly rolled back with DataIntegrityViolationException junit, spring, @Transactional
我的应用程序中有一个类别 -> 子类别 -> 产品层次结构。如果子类别没有产品,您可以删除它。如果子类别有产品,DAO 将抛出 DataIntegrityViolationException 并且事务应该回滚。
在我的测试中,我有:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {TestTransactionManagement.class})
public class BusinessSubCategoryCRUDTest {
@Autowired
public void setCRUD(BusinessSubCategoryCRUD crud) {
this.crud = crud;
}
// @Transactional
@Test
public void testDeleteBusinessSubCategoryInUseCanNotBeDeleted() {
final long id = 1;
BusinessSubCategory subCategoryBeforeDelete =
crud.readBusinessSubCategory(id);
final int numCategoriesBeforeDelete =
subCategoryBeforeDelete.getBusinessCategories().size();
try {
crud.deleteBusinessSubCategory(
new BusinessSubCategory(id, ""));
} catch (DataIntegrityViolationException e) {
System.err.println(e);
}
BusinessSubCategory subCategoryAfterDeleteFails =
crud.readBusinessSubCategory(id);
// THIS next assertion is the source of my angst.
// At this point the the links to the categories will have been
// been deleted, an exception will have been thrown but the
// Transaction is not yet rolled back if the test case (or test
// class) is marked with @Transactional
assertEquals(
numCategoriesBeforeDelete,
subCategoryAfterDeleteFails.getBusinessCategories().size());
}
}
但是,如果我取消对@Test 上面的@Transactional 的注释,它将失败。我认为 DAO 正在使用来自 @Test 的事务,因此在我检查以确保事务已回滚之前,事务不会回滚。
@Transactional(readOnly = false, propagation =
Propagation.REQUIRED)
public boolean deleteBusinessSubCategory(
BusinessSubCategory businessSubCategory) {
BeanPropertySqlParameterSource paramMap = new
BeanPropertySqlParameterSource(businessSubCategory);
namedJdbcTemplate.update(
DELETE_CATEGORY_SUB_CATEGORY_BY_ID_SQL,
paramMap);
return 0 != namedJdbcTemplate.update(
DELETE_SUB_CATEGORY_BY_ID_SQL,
paramMap);
}
那么,我如何让 DAO 代码仍然从它在 运行 中的上下文继承事务(在生产中它从它在 运行 中的服务继承事务)但仍然能够测试它。我想将 @Transactional 放在整个测试中 class,但这会使我的测试失败或不完整。
为了完整起见,这里是我的测试配置class。
@Configuration
@EnableTransactionManagement
public class TestTransactionManagement {
@Bean
public EmbeddedDatabase getDataSource() {
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
EmbeddedDatabase db = builder
.setType(EmbeddedDatabaseType.HSQL) //.H2 or .DERBY
.addScript("sql/create-db.sql")
.addScript("sql/create-test-data.sql")
.build();
return db;
}
@Bean
public DataSourceTransactionManager transactionManager() {
return new DataSourceTransactionManager(getDataSource());
}
@Bean
public BusinessSubCategoryCRUD getCRUD() {
return new BusinessSubCategoryCRUD(getDataSource());
}
}
“解决方案”或解决方法是在每次测试前重置数据库。然后在测试中不需要@T运行sactional,可以测试回滚,并且由于额外的数据库设置,测试套件运行稍微慢一些。
@Before
public void setUp() {
Connection conn = DataSourceUtils.getConnection(dataSource);
ScriptUtils.executeSqlScript(
conn, new ClassPathResource("sql/create-test-data.sql"));
DataSourceUtils.releaseConnection(conn, dataSource);
}
我的应用程序中有一个类别 -> 子类别 -> 产品层次结构。如果子类别没有产品,您可以删除它。如果子类别有产品,DAO 将抛出 DataIntegrityViolationException 并且事务应该回滚。
在我的测试中,我有:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {TestTransactionManagement.class})
public class BusinessSubCategoryCRUDTest {
@Autowired
public void setCRUD(BusinessSubCategoryCRUD crud) {
this.crud = crud;
}
// @Transactional
@Test
public void testDeleteBusinessSubCategoryInUseCanNotBeDeleted() {
final long id = 1;
BusinessSubCategory subCategoryBeforeDelete =
crud.readBusinessSubCategory(id);
final int numCategoriesBeforeDelete =
subCategoryBeforeDelete.getBusinessCategories().size();
try {
crud.deleteBusinessSubCategory(
new BusinessSubCategory(id, ""));
} catch (DataIntegrityViolationException e) {
System.err.println(e);
}
BusinessSubCategory subCategoryAfterDeleteFails =
crud.readBusinessSubCategory(id);
// THIS next assertion is the source of my angst.
// At this point the the links to the categories will have been
// been deleted, an exception will have been thrown but the
// Transaction is not yet rolled back if the test case (or test
// class) is marked with @Transactional
assertEquals(
numCategoriesBeforeDelete,
subCategoryAfterDeleteFails.getBusinessCategories().size());
}
}
但是,如果我取消对@Test 上面的@Transactional 的注释,它将失败。我认为 DAO 正在使用来自 @Test 的事务,因此在我检查以确保事务已回滚之前,事务不会回滚。
@Transactional(readOnly = false, propagation =
Propagation.REQUIRED)
public boolean deleteBusinessSubCategory(
BusinessSubCategory businessSubCategory) {
BeanPropertySqlParameterSource paramMap = new
BeanPropertySqlParameterSource(businessSubCategory);
namedJdbcTemplate.update(
DELETE_CATEGORY_SUB_CATEGORY_BY_ID_SQL,
paramMap);
return 0 != namedJdbcTemplate.update(
DELETE_SUB_CATEGORY_BY_ID_SQL,
paramMap);
}
那么,我如何让 DAO 代码仍然从它在 运行 中的上下文继承事务(在生产中它从它在 运行 中的服务继承事务)但仍然能够测试它。我想将 @Transactional 放在整个测试中 class,但这会使我的测试失败或不完整。
为了完整起见,这里是我的测试配置class。
@Configuration
@EnableTransactionManagement
public class TestTransactionManagement {
@Bean
public EmbeddedDatabase getDataSource() {
EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
EmbeddedDatabase db = builder
.setType(EmbeddedDatabaseType.HSQL) //.H2 or .DERBY
.addScript("sql/create-db.sql")
.addScript("sql/create-test-data.sql")
.build();
return db;
}
@Bean
public DataSourceTransactionManager transactionManager() {
return new DataSourceTransactionManager(getDataSource());
}
@Bean
public BusinessSubCategoryCRUD getCRUD() {
return new BusinessSubCategoryCRUD(getDataSource());
}
}
“解决方案”或解决方法是在每次测试前重置数据库。然后在测试中不需要@T运行sactional,可以测试回滚,并且由于额外的数据库设置,测试套件运行稍微慢一些。
@Before
public void setUp() {
Connection conn = DataSourceUtils.getConnection(dataSource);
ScriptUtils.executeSqlScript(
conn, new ClassPathResource("sql/create-test-data.sql"));
DataSourceUtils.releaseConnection(conn, dataSource);
}