java 测试异步方法调用

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7594354/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 20:37:46  来源:igfitidea点击:

Testing an async method call

javatestingjunitintegration-testing

提问by Kim L

Below is a simplified setup of my application. It has a class Foobar which calls on a facade method for fetching data. The facade then calls on a web service to actually get the data and then manipulates the data a bit and then returns it to Foobar.

以下是我的应用程序的简化设置。它有一个 Foobar 类,它调用一个 Facade 方法来获取数据。Facade 然后调用 Web 服务来实际获取数据,然后对数据进行一些操作,然后将其返回给 Foobar。

Now because the web service might take a good while to run, the method call to the facade needs to be asynchronous. Hence the facade's method doesn't have a return value, but instead, the method uses a callback object. Look at the example and continue reading below.

现在因为 Web 服务可能需要很长时间才能运行,所以对 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);
    }

}

I have two ways of making the facade's method asynchronous, either by creating a thread manually or by using springs @Async annotation.

我有两种方法可以使 Facade 的方法异步,一种是手动创建线程,另一种是使用 springs @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) {
        ....    
    }
}

My problem is that I now need to write an integration test for this chain of method calls. I think I need to force the async facade call to be synchronous when the integration test is ran, otherwise I won't know for sure when I can do the appropriate asserts. The only idea for making the method call synchronous is to use manually handled threads AND making the threading conditional (so, for testing purposes, I have an if clause which determines if the facade method should be ran in a separate thread or not).

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

However, I have a feeling that there could be a better solution to my problem, whether it be a better way of forcing the method to me synchronous, eg with spring, or by testing the multithreading on some way.

但是,我有一种感觉,我的问题可能有更好的解决方案,无论是强制方法同步给我的更好方法,例如使用 spring,还是通过某种方式测试多线程。

This is where I need your suggestions, how would you solve my problem? Note, I'm using junit for both unit and integration tests.

这是我需要您的建议的地方,您将如何解决我的问题?请注意,我将 junit 用于单元测试和集成测试。

回答by stratwine

Simple solution would be to return a Future object like this,

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

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

And in your test, take the future reference and call the get()method.

在您的测试中,获取未来参考并调用get()方法。

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

This blog postshows a sample.

这篇博文展示了一个示例。

回答by Barend

When JUnit testing stuff like this, I use a testing callback with a CountDownLatchthat gets counted down by the callback and await()ed by the test method.

当 JUnit 测试这样的东西时,我使用了一个测试回调,CountDownLatch它被回调倒计时并被await()测试方法 ed。

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

If the callback is invoked (asynchronously) by the code under test, the latch returns trueand the test passes. If the callback doesn't get invoked, the test times out after thirty seconds and the assertion fails.

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