Spring测试事务线程

时间:2018-06-05 13:22:25

标签: spring transactions spring-data-jpa integration-testing

我已将同步过程转移到异步,现在我有一些麻烦来维护集成测试。这似乎与以下事实相关:当您在@Transactional方法中创建新线程,然后调用新的@Transactional时,Spring会创建一个新事务。

在集成测试期间,@ Transnsaction测试会出现问题。由于测试配置中的TransactionalTestExecutionListener,似乎在测试完成之前回滚了线程事务。

我已经尝试了很多类似的东西 - 自动装配EntityManager并在线程完成后手动刷新 - 在测试方法中使用@Rollback而不是@Transactional - 使用TestTransaction管理事务 - 一起使用@Rollback和TestTransaction

以下是简化的源代码:

public interface MyService{
    public void doThing(someArgs...);

    public void updateThings(someArgs...);
}

@Service
public class MyServiceImpl implements MyService{

    @Autowired
    private AsynchronousFutureHandlerService futureService;

    @Autowired
    @Qualifier("myExecutorService")
    private ScheduledExecutorService myExecutorService;

    @Transactional
    @Override
    public void doThing(someArgs...){
        doThingAsync(someArgs...);
    }

    private void doThingAsync(someArgs...){
        AsynchronousHandler runnable = applicationContext.getBean(
                AsynchronousHandler.class, someArgs...);

        //as we are executing some treatment in a new Thread, a new transaction is automatically created
        Future<?> future = myExecutorService.submit(runnable);

        //keep track of thread execution
        futureService.addFutures(future);
    }

    @Override
    @Transactional
    public void updateThings(someArgs...){
        //do udpate stuff
    }
}

/**
*   very basic solution to improve later to consult thread state
*/
@Service
public class AsynchronousFutureHandlerService {

    //TODO : pass to ThreadSafe collection
    private List<Future<?>> futures = new ArrayList<>();

    public void addTheoreticalApplicationFuture(Future<?> future){
        futures.add(future);
        this.deleteJobsDone();
    }

    public boolean isThreadStillRunning(){
        boolean stillRunning = false;
        for(Future<?> f : futures){
            if(!f.isDone()){
                stillRunning = true;
                break;
            }
        }
        return stillRunning;
    }

    public void deleteJobsDone(){
        this.futures.removeIf(f -> f.isDone());
    }
}

@Component
@Scope("prototype")
public class AsynchronousHandler implements Runnable {

    @Autowired
    private MyService myService;

    @Override
    public void run() {
        myService.updateThings(...); //updates data in DB
        ...
    }
}

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = TestConfiguration.class)
@TestExecutionListeners({ DependencyInjectionTestExecutionListener.class, DataSetTestExecutionListener.class,
        TransactionalTestExecutionListener.class })
@DataSet(dbType = DBType.H2, locations = { "classpath:dataset.xml" })

public class MyServiceTest{

    @Autowired
    private MyService myService;

    @Autowired
    private AsynchronousFutureHandlerService futureService;

    @Test
    @Transactional
    public void test_doThings(){
        myService.doThings(someArgs...);
        waitUpdateFinish();

        Assert.assertEquals(...); //fails here because Thread transaction has been rollbacked
    }

    private void waitUpdateFinish() throws InterruptedException{
        while(futureService.isThreadStillRunning()){
            Thread.sleep(500);
        }
    }
}

0 个答案:

没有答案