簡體   English   中英

使用EasyMock測試多線程(CompletableFuture)

[英]test multithreading (CompletableFuture) with EasyMock

我想為方法添加測試,其中包含CompletableFuture:

 public void report(List<String> srcList) {
        if (srcList != null) {
            ...
            CompletableFuture.runAsync(() ->
               ....
               srcList.forEach(src-> downloader.send(url)));
        }
 }

我想測試一下,調用了send方法。 我的測試看起來像:

 @Test
 public void _test() {
        List<String> events = new ArrayList();
        events.add("http://xxxx//");
        events.add("http://xxxx//");

        expect(downloader.send(events.get(0))).andReturn("xxx").times(2);
        replay(downloader);
        eventReporter.report(events);

        verify(downloader);
 }

而且我得到了這樣的錯誤Downloader.send("http://xxxx//"): expected: 2, actual: 0

避免此錯誤的一種方法是設置Thread.sleep(100); 暫停。 然后線程將等待並驗證該方法是否已調用。 但這會增加測試時間。

還有其他方法可以使用EasyMock測試多線程嗎?

Thread.sleep()方法對異步代碼進行單元測試是一個不好的做法,因為如果您設置了較長的睡眠時間並編寫了很少的測試,則即使它可以正常工作,測試也會變得不穩定和閃爍(運行3次2次通過並失敗1次)這樣,您遇到的執行時間可能會超過數十秒。 為了完成此任務,您需要將代碼的異步部分與同步解耦。 示例如何做:

class Service {

    private Downloader downloader;
    private ExecutorService service;

    public Service (Downloader downloader, ExecutorService service) {
        //set variables
    }

    public void doWork(List<String> list) {
        for (String item : list) {
            service.submit(() -> {
                downloader.download(item);
            });
        }
    }
}

ExecutorService是接口,我們需要使我們的服務保持同步

class SycnronousService impliments ExecutorService {

    //methods empty implementations

    public void submit(Runnable runnable) {
        runnable.run(); //run immediately
    }

    //methods empty implementations
}

public class ServiceTest {

    public void shouldPassAllItemsToDownloader() {
        Downloader mockDownloader = AnyMockFramework.mockIt();
        Service service = new Service(mockDownloader, new SycnronousService());
        List<String> tasks = Arrays.asList("A", "B");
        service.doWork(tasks);
        verify(mockDownloader).download("A"); //verify in your way with EasyMock
        verify(mockDownloader).download("B"); //verify in your way with EasyMock
        // no more Timer.sleep() , test runs immeadetely  
    }

}

您需要將CompletableFuture替換為我的示例中的內容,因為無法以這種方式對代碼進行單元測試。 稍后在您的應用程序中,您將能夠將SycnronousService替換為異步實現,並且所有操作都將按預期進行。

我同意@ joy-dir的回答。 您可能應該按照她說的做,以簡化測試。

為了完整起見,您的問題是在實際完成任務之前調用了verify 您可以做很多事情。

一種是循環verify

@Test
public void test() throws Exception {
    List<String> events = new ArrayList();
    events.add("http://xxxx//");
    events.add("http://xxxx//");

    expect(downloader.send(events.get(0))).andReturn("xxx").times(2);
    replay(downloader);
    report(events);

    for (int i = 0; i < 10; i++) {
        try {
            verify(downloader);
            return;
        } catch(AssertionError e) {
            // wait until it works
        }
        Thread.sleep(10);
    }
    verify(downloader);
}

成功的話,它不會長時間無休止的睡覺。 但是,您確實需要確保等待足夠長的時間以防止測試不穩定。

另一個解決方案實際上是使用runAsync返回的CompletableFuture 我更喜歡這種解決方案。

public CompletableFuture<Void> report(List<String> srcList) {
    if (srcList != null) {
        return CompletableFuture.runAsync(() -> srcList.forEach(src-> downloader.send(src)));
    }
    return CompletableFuture.completedFuture(null);
}

@Test
public void test2() throws Exception {
    List<String> events = new ArrayList();
    events.add("http://xxxx//");
    events.add("http://xxxx//");

    expect(downloader.send(events.get(0))).andReturn("xxx").times(2);
    replay(downloader);
    CompletableFuture<Void> future = report(events);

    future.get(100, TimeUnit.MILLISECONDS);

    verify(downloader);
}

最后,有一種破解方法。 您詢問公用池是否已完成。 這是駭客的,因為其他東西可能會使用它。 所以它很可愛,但我不會推薦它。

@Test
public void test3() throws Exception {
    List<String> events = new ArrayList();
    events.add("http://xxxx//");
    events.add("http://xxxx//");

    expect(downloader.send(events.get(0))).andReturn("xxx").times(2);
    replay(downloader);
    report(events);

    while(!ForkJoinPool.commonPool().isQuiescent()) {
        Thread.sleep(10);
    }

    verify(downloader);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM