@Transactional 无法使用@DataJpaTest 进行测试

问题描述 投票:0回答:1

我坚持围绕

@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
机制?

java spring spring-data-jpa
1个回答
0
投票

主要问题是

@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
© www.soinside.com 2019 - 2024. All rights reserved.