我坚持围绕
@Transactional
方法进行单元测试。问题是我无法测试@DataJpaTest
内部的回滚机制。 @RestController
public class MyController {
private final MyRepository myRepository;
private final MyService myService;
public MyController(MyRepository myRepository, MyService myService) {
this.myRepository = myRepository;
this.myService = myService;
}
@GetMapping
@Transactional
public String justForTest() {
var myEntity = new MyEntity().setStatus("NEW");
myRepository.save(myEntity);
try {
myService.throwsAnRuntimeException();
// on service succeed
myEntity.setStatus("SUCCESS");
} catch (Exception e) {
// on service fail
myEntity.setStatus("FAIL");
throw e;
} finally {
myRepository.save(myEntity);
}
return "this is the end";
}
}
@Data
@Entity
@Accessors(chain = true)
public class MyEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String status;
}
@Repository
public interface MyRepository extends JpaRepository<MyEntity, Long> {
}
@Service
public class MyService {
public void throwsAnRuntimeException() {
throw new RuntimeException("Rollbacks not working!");
}
}
因此,当我以通常的方式(使用本地 postgresql 数据库)运行此代码时,它会按预期工作:当我在 localhost:8080 上调用查询时,响应为 500 并且数据库中的表完全为空,如下所示这是预期的,因为当
@Transactional
方法以 RuntimeException
终止时,它会执行 rollback
整个交易(即使最终块有 repository.save()
)。 @DataJpaTest
(测试范围的类路径中的 h2 数据库)测试此代码时:
@DataJpaTest
class MyControllerTest {
@Autowired
private MyRepository repository;
@Test
void justForTest() {
var controller = new MyController(repository, new MyService());
assertThrows(RuntimeException.class, controller::justForTest);
var afterTestEntities = repository.findAll();
System.out.println(afterTestEntities.getFirst());
assertEquals(0, afterTestEntities.size());
}
}
我测试失败,我的调试 println 显示我的实体处于失败状态
MyEntity(id=1, status=FAIL)
。但我认为,就像在实际运行中一样,由于事务回滚,我应该有一个空的存储库。
所以,我的主要问题是如何在单元测试中测试
@Transactional
机制?
主要问题是
@DataJpaTest
不处理控制器方法上的@Transactional注释,因为控制器在测试设置中不是由Spring管理的,尝试添加事务测试配置:
@Configuration
@EnableTransactionManagement
public class TransactionalTestConfig {
@Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory emf) {
return new JpaTransactionManager(emf);
}
}
然后在你的测试课中:
@DataJpaTest
@Import({TransactionalTestConfig.class, MyService.class})
class MyControllerTest {
@Autowired
private MyRepository repository;
@Autowired
private PlatformTransactionManager transactionManager;
@Autowired
private MyService myService;
@Test
void testTransactionalRollback() {
// Create a transaction template
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
// Execute the test within a transaction
assertThrows(RuntimeException.class, () -> {
transactionTemplate.execute(status -> {
var controller = new MyController(repository, myService);
controller.justForTest();
return null;
});
});
// Verify the rollback
var afterTestEntities = repository.findAll();
assertEquals(0, afterTestEntities.size());
}
}
在您的财产中:
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
logging.level.org.springframework.transaction=TRACE