【问题标题】:Testing that delete is correctly rolled back with DataIntegrityViolationException junit, spring, @Transactional使用 DataIntegrityViolationException junit、spring、@Transactional 测试删除是否正确回滚
【发布时间】:2016-04-24 06:11:04
【问题描述】:

我的应用程序中有一个类别 -> 子类别 -> 产品层次结构。如果子类别没有产品,您可以删除它。如果 subCategory 有产品,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 放在整个测试类上,但这会使我的测试失败或不完整。

为了完整起见,这是我的测试配置类。

@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());
  }
}

【问题讨论】:

  • 如果我取消注释@Test 上方的事务,它会失败。它在哪里失败? (您的测试中有 4 个断言)
  • 最后一个断言失败...当它从数据库中读取子类别时,类别已被删除,因此它需要 1 个类别并找到 0
  • 我编辑了其他断言以澄清问题。谢谢@ben75
  • 您使用的是基于 AspectJ 的事务管理还是基于 Spring 代理?
  • 或者,如果你不知道我上一个问题的答案,你可以通过打破 Spring 的 TransactionManagementConfigurationSelector.selectImports(...) 方法来告诉它,并报告在运行时选择了 switch 语句的哪个分支。

标签: spring junit transactions


【解决方案1】:

“解决方案”或解决方法是在每次测试之前重置数据库。然后在测试中不需要@Transactional,可以测试回滚,并且由于额外的数据库设置,测试套件的运行速度略慢。

@Before
public void setUp() {
    Connection conn = DataSourceUtils.getConnection(dataSource);
    ScriptUtils.executeSqlScript(
        conn, new ClassPathResource("sql/create-test-data.sql"));
    DataSourceUtils.releaseConnection(conn, dataSource);
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-03-13
    • 1970-01-01
    • 1970-01-01
    • 2021-08-16
    • 2019-11-16
    • 1970-01-01
    • 2017-08-01
    • 2018-06-04
    相关资源
    最近更新 更多