随着处理器比以往包装更多的内核,并发编程已成为最有效利用它们的最前沿。但是,事实是并发程序的设计,编写,测试和维护要困难得多。因此,如果我们毕竟可以为并发程序编写有效且自动化的测试用例,则可以解决其中的大部分问题。
CountDownLatch
@Test public void should_publish_an_article_using_count_down_latch_to_fix() throws InterruptedException { //Arrange Article article = Article.newBuilder() .withBody("learning how to test multithreaded java code") .withId(1) .withTitle("title").build();
CountDownLatch countDownLatch = new CountDownLatch(1); when(this.articleRepository.findById(1)).thenReturn(article); doAnswer(invocationOnMock -> { System.out.println("Sending mail !!!"); countDownLatch.countDown(); return null; }).when(this.emailSender).sendEmail(anyString(), anyString());
//Act boolean publish = this.articlePublisher.publish(1);
//Assert assertThat(publish).isTrue(); verify(this.articleRepository).findById(1); countDownLatch.await(); verify(this.emailSender).sendEmail("Article Published With Id " + 1 , "Published an article with Article Title " + "title"); verifyNoMoreInteractions(this.articleRepository, this.emailSender); }
|
我们使用CountDownLatch,以便主线程应等待,直到调用send email方法。我们调用countDownLatch的countDown方法进行暂停倒计时,直至awat()方法唤醒。
使用循环屏障Cyclic Barrier
@Test public void should_publish_an_article_using_cyclic_barrier_to_fix() throws BrokenBarrierException, InterruptedException { //Arrange Article article = Article.newBuilder() .withBody("learning how to test multithreaded java code") .withId(1) .withTitle("title").build();
CyclicBarrier cyclicBarrier = new CyclicBarrier(2, () -> System.out.println("Barrier opening")); when(this.articleRepository.findById(1)).thenReturn(article); doAnswer(invocationOnMock -> { System.out.println("sending mail !!!"); cyclicBarrier.await(); return null; }).when(this.emailSender).sendEmail(anyString(), anyString()); //Act boolean publish = this.articlePublisher.publish(1);
//Assert assertThat(publish).isTrue(); verify(this.articleRepository).findById(1); cyclicBarrier.await(); verify(this.emailSender).sendEmail("Article Published With Id " + 1 , "Published an article with Article Title " + "title"); verifyNoMoreInteractions(this.articleRepository, this.emailSender); }
|
循环屏障使这两个并发任务同步进行。当emailSender线程的sendEmail方法和主线程同步时,将打开屏障。随着线程数量的增加,它们可能交错的方式也呈指数增长。根本不可能弄清楚所有这样的交错并对其进行测试。我们必须依靠工具为我们进行相同或相似的工作。幸运的是,其中有一些工具可以使我们的生活更轻松。