Spring Boot 在多线程环境的集成测试类上使用@Transactional注解时出现问题

brccelvz  于 2022-11-23  发布在  Spring
关注(0)|答案(2)|浏览(257)

当我在一个新线程中运行调用JPA存储库的代码的集成测试时,我得到的数据是在启动PostgreSQL容器期间填充的,我不能从class test(@Sql(scripts =“data.sql”)上面的脚本接收数据。
但是,当我删除测试上面的@Transactional注解时,我可以从测试和测试容器的SQL脚本中获取数据。
我的问题是,在多线程环境中,是否有可能在不删除@Transactional注解的情况下从测试脚本中获取数据?谢谢您的回答!

**应用程序堆栈:**Sping Boot 2.1v+测试容器PostgreSQL 1.10.3v+ JUnit 4.12v
数据库测试容器配置

@TestConfiguration
public class DatabaseTestConfig {

    private static JdbcDatabaseContainer PSQL;

    static {

        PSQL = (PostgreSQLContainer) new PostgreSQLContainer("mdillon/postgis:9.4").withUsername("test")
                .withPassword("test")
                .withDatabaseName("test");
        PSQL.start();

        Arrays.asList("main_data.sql")
                .forEach(DatabaseTestConfig::restoreDump);

        /*
           set db properties
        */

    }

    public void restoreDump(String fileName){
         /*
           insert sql data 
           PSQL.copyFileToContainer(fileName)...
        */
    }

}

基本集成测试类

@RunWith(SpringRunner.class)
@SpringBootTest(classes = { DatabaseTestConfig.class, ProjectApplication.class })
@ActiveProfiles("test-int")
@AutoConfigureMockMvc
@Sql(scripts = "classpath:extra_data.sql") // insert some extra data for all integration tests
public abstract class AbstractIntTest {
    @Autowired
    protected MockMvc mockMvc;

在发生任何事情时调用服务的集成测试

@Transactional
public class SomeIntegrationTest extends AbstractIntTest {

    @Before
    public void setUp() throws IOException {
      //...
    }

    @Test
    public void callServiceTest() throws Exception {
      //mockMvc.perform(post(ENDPOINT_URL)
    }

具有简化逻辑的服务

@Service
@AllArgsConstructor
public class SomeService {
    private final SomeJpaReporistory repo;
    private final ExecutorService executor;

    @Override
    @Transactional
    public SomeData call(){
        return CompletableFuture.supplyAsync(() -> {
            return repo.findAll(); 
        }, executor).exceptionally(e -> {
            throw new BadRequestException(e.getMessage());
        });
    }
mkshixfv

mkshixfv1#

当您将测试设为交易时,extra_data.sql中的SQL查询会在交易中执行。该交易会系结至特定的执行绪,并在执行测试方法之前开始,在测试方法完成之后回复:
1.开始交易
1.执行extra_data.sql
1.调用测试方法
1.回滚事务处理
在步骤3中,由于服务使用了supplyAsync,您将在单独的线程上调用repo.findAll()。由于事务绑定到特定线程,因此此findAll()调用不是执行extra_data.sql的事务的一部分。为了能够读取由extra_data.sql添加的数据,它必须能够读取未提交的更改并执行脏读。Postgres不支持读取未提交的隔离级别,因此这是不可能的。
您需要重新审视如何用测试数据填充数据库,或者如何在测试中使用事务。也许您可以像main_data.sql一样将extra_data.sql应用到数据库,以便在执行任何测试和开始任何事务之前,它始终处于适当位置。

4nkexdtk

4nkexdtk2#

我是这样解决这个问题的:

@Test
@Transactional
@Sql(scripts = "/db/extra_data.sql",
config = @SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED))
void test() {
    // extra_data.sql are executed before this test is run.
}

相关问题