测试异步方法调用

Kim*_*m L 5 java testing junit integration-testing

以下是我的应用程序的简化设置.它有一个Foobar类,它调用facade方法来获取数据.然后,外观调用Web服务来实际获取数据,然后稍微操作数据,然后将其返回到Foobar.

现在因为Web服务可能需要很长时间才能运行,所以对Facade的方法调用需要是异步的.因此,facade的方法没有返回值,而是使用回调对象.查看示例并继续阅读下面的内容.

public class Foobar {
    private List<DTO> dtos;

    @Autowired
    private Facade facade;

    public void refresh() {
        facade.refreshFoobar(new CallBack() {
            public void dataFetched(List<DTO> dtos) {
                setDtos(dtos);
            }

        });
    }    

    public void setDtos(List<DTO> dtos) {
        this.dtos = dtos;
    }
}


public class Facade {

    ...

    public void refreshFoorbar(CallBack cb) {
        // Fetch data from a web service
        List<DTO> dtos = webService.getData();  
        // Manipulate DTOs
        ....
        // call on the callback method
        cb.dataFecthed(dtos);
    }

}
Run Code Online (Sandbox Code Playgroud)

我有两种方法可以通过手动创建线程或使用spring @Async注释来使facade的方法异步.

public class Facade {

    public void refreshFoorbar(CallBack cb) {
        new Thread() {

            @Override
            public void run() {
                ....
            }

        }.start();

    }
}

// ... OR ...

public class Facade {

    @Async
    public void refreshFoorbar(CallBack cb) {
        ....    
    }
}
Run Code Online (Sandbox Code Playgroud)

我的问题是我现在需要为这个方法调用链编写一个集成测试.我认为在运行集成测试时我需要强制异步外观调用是同步的,否则我无法确定何时可以执行相应的断言.使方法调用同步的唯一想法是使用手动处理的线程并使线程成为条件(因此,出于测试目的,我有一个if子句,它确定是否应该在单独的线程中运行facade方法).

但是,我觉得可以有一个更好的解决方案来解决我的问题,无论是将这种方法强制同步的更好方法,例如使用spring,还是通过某种方式测试多线程.

这是我需要你的建议的地方,你将如何解决我的问题?注意,我正在使用junit进行单元测试和集成测试.

str*_*ine 12

简单的解决方案是返回这样的Future对象,

@Async
public Future<String> refreshFoorbar(CallBack cb) {
    yourHeavyLifting(); //asynchronous call
    return new AsyncResult<String>("yourJobNameMaybe");   
}
Run Code Online (Sandbox Code Playgroud)

在测试中,采用将来的引用并调用get()方法.

future.get(); // if its not already complete, waits for it to complete
assertTrue(yourTestCondition)
Run Code Online (Sandbox Code Playgroud)

这篇博客文章展示了一个样本.


Bar*_*end 6

当JUnit测试这样的东西时,我会使用一个测试回调,CountDownLatch它通过回调倒计数await()并由测试方法编辑.

private static class TestingCallback implements Callback {
    private final CountDownLatch latch;
    public TestingCallback(CountDownLatch latch) {
        this.latch = latch;
    }
    @Override public void onEvent() {
        this.latch.countDown();
    }
}

@Test
public void testCallback() {
    final CountDownLatch latch = new CountDownLatch(1);

    classUnderTest.execute( new TestCallback(latch) );

    assertTrue(latch.await(30, TimeUnit.SECONDS));
}
Run Code Online (Sandbox Code Playgroud)

如果被测试的代码调用(异步)回调,则锁存器返回true并且测试通过.如果没有调用回调,则测试在30秒后超时并且断言失败.