如何使用 Spring 4 和注释编写单元测试来验证异步行为?

问题描述 投票:0回答:2

如何使用 Spring 4 和注释编写单元测试来验证异步行为?

由于我习惯了 Spring 的(旧的)xml 风格),我花了一些时间才弄清楚这一点。所以我想我回答自己的问题是为了帮助别人。

spring unit-testing asynchronous
2个回答
7
投票

首先是公开异步下载方法的服务:

@Service
public class DownloadService {
    // note: placing this async method in its own dedicated bean was necessary
    //       to circumvent inner bean calls
    @Async
    public Future<String> startDownloading(final URL url) throws IOException {
        return new AsyncResult<String>(getContentAsString(url));
    }

    private String getContentAsString(URL url) throws IOException {
        try {
            Thread.sleep(1000);  // To demonstrate the effect of async
            InputStream input = url.openStream();
            return IOUtils.toString(input, StandardCharsets.UTF_8);
        } catch (InterruptedException e) {
            throw new IllegalStateException(e);
        }
    }
}

接下来的测试:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class DownloadServiceTest {

    @Configuration
    @EnableAsync
    static class Config {
        @Bean
        public DownloadService downloadService() {
            return new DownloadService();
        }
    }

    @Autowired
    private DownloadService service;

    @Test
    public void testIndex() throws Exception {
        final URL url = new URL("http://spring.io/blog/2013/01/16/next-stop-spring-framework-4-0");
        Future<String> content = service.startDownloading(url);
        assertThat(false, equalTo(content.isDone()));
        final String str = content.get();
        assertThat(true, equalTo(content.isDone()));
        assertThat(str, JUnitMatchers.containsString("<html"));
    }
}

2
投票

如果您在 Java 8 中使用相同的示例,您也可以使用 CompletableFuture 类,如下所示:

    @Service
    public class DownloadService {
            
            
            @Async
            public CompletableFuture<String> startDownloading(final URL url) throws IOException {

             CompletableFuture<Boolean> future = new CompletableFuture<>();
    
              Executors.newCachedThreadPool().submit(() -> {
                getContentAsString(url);
                future.complete(true);
                return null;
               });
                   return future;
            }
        
           private String getContentAsString(URL url) throws IOException {
              try {
                Thread.sleep(1000);  // To demonstrate the effect of async
                 InputStream input = url.openStream();
                 return IOUtils.toString(input, StandardCharsets.UTF_8);
             } catch (InterruptedException e) {
                 throw new IllegalStateException(e);
             }
         }
   }

现在测试:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class DownloadServiceTest {

    @Configuration
    @EnableAsync
    static class Config {
        @Bean
        public DownloadService downloadService() {
            return new DownloadService();
        }
    }

    @Autowired
    private DownloadService service;

    @Test
    public void testIndex() throws Exception {
        final URL url = new URL("http://spring.io/blog/2013/01/16/next-stop-spring-framework-4-0");

        CompletableFuture<Boolean> content = service.startDownloading(url);

        content.thenRun(() -> {
           assertThat(true, equalTo(content.isDone()));
           assertThat(str, JUnitMatchers.containsString("<html"));
        });

        // wait for completion
        content.get(10, TimeUnit.SECONDS);
        
    }
}

请注意,如果未指定超时,并且出现任何问题,测试将“永远”继续进行,直到 CI 或您将其关闭。

© www.soinside.com 2019 - 2024. All rights reserved.