我在Stackoverflow和其他地方看到了类似的例子,但它对我不起作用.我使用spring boot数据jpa,junit5,h2数据库并试图进行简单的验收测试.测试OK,但我希望数据库中的更改在测试后恢复,但它们仍然保持提交状态.在测试中,我在数据库中创建一行,在其他测试中,我删除一行. 下面是我的代码层: 测试:

@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)

public class ControllerAcceptanceTest {

@LocalServerPort
int randomServerPort;
private RestTemplate restTemplate;
private String url;

@BeforeEach
void setUp() {
    restTemplate = new RestTemplate();
}

@Test
@Transactional
@Rollback
void testCreate() throws Exception {
    url = "http://localhost:" + randomServerPort + "/api/wallet/create";
    UUID uuid = UUID.randomUUID();
    PlayerDto playerDto = new PlayerDto();
    playerDto.setPlayerId(String.valueOf(uuid));
    ResponseEntity responseEntity = restTemplate.postForEntity(url, playerDto, Object.class);
    assertEquals(HttpStatus.OK, responseEntity.getStatusCode());
}

@Test
@Transactional
@Rollback
void testDelete() throws Exception {
    url = "http://localhost:" + randomServerPort + "/api/wallet/delete";
    WalletDto walletDto = new WalletDto();
    walletDto.setWalletId("1");
    ResponseEntity responseEntity = restTemplate.postForEntity(url, walletDto, Object.class);
    assertEquals(HttpStatus.OK, responseEntity.getStatusCode());
}

}

控制器:

@PostMapping (path = "/create", consumes = MediaType.APPLICATION_JSON_VALUE)
public void createWallet(@RequestBody PlayerDto playerDto) throws PlayerIdRedundantException {
    walletService.createWallet(playerDto.getPlayerId());
}
@PostMapping (path = "/delete", consumes = MediaType.APPLICATION_JSON_VALUE)
public void deleteWallet(@RequestBody WalletDto walletDto) throws PlayerIdRedundantException {
    walletService.deleteWallet(walletDto.getWalletId());
}

服务:

@Transactional
public void createWallet(String playerId) throws PlayerIdRedundantException {
    checkPlayerId(playerId);
    Wallet wallet = Wallet.builder()
            .playerId(playerId)
            .balance(BigDecimal.valueOf(0))
            .build();
    walletRepository.save(wallet);
}
@Transactional
public void deleteWallet(String walletId) throws PlayerIdRedundantException {

    Wallet wallet = walletRepository.findById(Long.valueOf(walletId)).orElseThrow();
    walletRepository.delete(wallet);
}

应用程序.属性:

spring.h2.console.enabled=true
spring.datasource.url=jdbc:h2:file:./src/main/resources/data/testdb;AUTO_SERVER=TRUE;OLD_INFORMATION_SCHEMA=TRUE
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.generate-ddl=true

推荐答案

这是测试Spring Boot应用程序时事务管理的常见trap .

您必须区分测试管理事务和Spring管理事务.当在测试中的方法/类上使用@Transactional时,Spring Test将把测试执行包装在一个事务中,并在默认情况下回滚对数据库的任何更改(因此@Rollback是多余的).

这只适用于您在此测试托管事务中直接对数据库进行的更改,因此,如果您想在测试方法directly中调用myRepository.save().

在您的示例中,您正在编写一项验收测试,使用HTTP从外部测试您的应用程序,就像您的客户端/前端在生产过程中所做的那样.当您的调用到达您的服务时,这将触发一个由Spring管理的事务,并在调用完成后提交更改.

因此,您实际上有两个事务:在测试执行期间托管的测试和启动的应用程序由Spring管理的inside.从您的测试中,您无法访问Spring管理的事务,并且您的回滚方法将不会在实际应用程序上下文中拥有该事务的句柄.

要解决这个问题,你可以拨打BeforeEachAfterEach.中的repository.deleteAll()

此外,有关更详细的解释,请参考TransactionalTestExecutionListener的Javadoc.

Java相关问答推荐

在现有Json文件中添加新记录

Selenium Java:无法访问IFRAME内部的元素

在AnyLogic中增加变量计数

需要一个找不到的jakarta.sistence.EntityManager类型的Bean

JavaFX如何在MeshView中修复多个立方体?

在Spring Boot应用程序中导致";MediaTypeNotSupportdException&qot;的映像上载

在Spring终结点中,是否可以同时以大写和小写形式指定枚举常量?

Helidon 4和Http API

继续收到错误SQLJDBC EXCEPTION执行";org.springframework.dao.InvalidDataAccessResourceUsageException:&

如何使用log4j2(Json)记录由";异常引起的所有";?

我怎样才能让IntelliJ标记toString()的任何实现?

Lombok@Nonnull是否也对供应商有影响?

RestTemplate Bean提供OkHttp3ClientHttpRequestFactory不支持Spring Boot 3中的请求正文缓冲

如何使用jooq更新记录?

如何制作回文程序?

基于距离的APACHE POI公式判断

PhantomReference无法访问时会发生什么?

获取401未经授权,即使在标头中设置了浏览器名称和cookie

JavaFX中ListView中的问题

在JPanel上使用GridBagLayout并将JButton放在里面时出现问题