测试异步方法调用

5
以下是我应用程序的简化设置。它有一个名为Foobar的类,调用一个门面方法来获取数据。然后,门面调用Web服务实际获取数据,稍微处理一下,然后将其返回给Foobar。
由于Web服务可能需要一段时间才能运行,因此对门面的方法调用需要是异步的。因此,门面方法没有返回值,而是使用回调对象。请查看下面的示例并继续阅读。
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);
    }

}

我有两种方法使外观的方法异步化,一种是手动创建线程,另一种是使用Spring的@Async注释。

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) {
        ....    
    }
}

我的问题是,我现在需要为这个方法链编写一个集成测试。我认为在运行集成测试时,需要将异步门面调用强制变成同步,否则我就不知道何时可以进行适当的断言。唯一想出的使方法调用变为同步的方法是使用手动处理的线程,并且将线程条件化(因此,为了测试目的,我有一个if子句,确定门面方法是否应在单独的线程中运行)。
然而,我有一种感觉,可能有更好的解决方案,无论是通过更好的方式强制方法变为同步,例如Spring,还是通过某种方式测试多线程。
这就是我需要您的建议的地方,您会如何解决我的问题?请注意,我对单元测试和集成测试都使用Junit。

我不确定这是否是正确的方法。通常测试异步条件只需启动异步任务,等待一段时间并测试任务是否已完成。 - SJuan76
2个回答

12

简单的解决方案是返回一个类似这样的 Future 对象:

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

在您的测试中,获取Future引用并调用get()方法。

future.get(); // if its not already complete, waits for it to complete
assertTrue(yourTestCondition)

这个博客文章展示了一个样例。


请参考以下链接:http://www-01.ibm.com/support/knowledgecenter/SSCKBL_8.5.5/com.ibm.websphere.nd.multiplatform.doc/ae/tejb_clientcode.html - Kalpesh Soni

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));
}

如果回调被测试代码(异步地)调用,闩锁将返回true,测试通过。如果回调没有被调用,测试会在30秒后超时并且断言失败。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接