java - 如何使用 mockito 测试运行异步线程的方法

标签 java multithreading junit completable-future junit-jupiter

我有以下要测试的代码:

@Slf4j
@Component
public class DispatcherTask {

    private final MyClassService myClassService;
    private final SreamingService streamingService;
    private ExecutorService executor = Executors.newCachedThreadPool();
    private final Set < String > dispatching = new ConcurrentSkipListSet < > ();
    private final RetryPolicy < Object > httpRetryPolicy;

    public DispatcherTask(MyClassService myClassService, SreamingService streamingService) {
        this.myClassService = myClassService;
        this.streamingService = streamingService;
        this.httpRetryPolicy = new RetryPolicy < > ()
            .handle(HttpClientErrorException.class)
            .onRetriesExceeded(e - > log.warn("Max retries have been exceeded for http exception"))
            .withBackoff(2, 3, ChronoUnit.SECONDS)
            .withMaxRetries(5);
    }

    public void initMethod(MyClass myclass) {
        Failsafe.with(httpRetryPolicy).with(executor)
            .onFailure((e) - > {
                // TODO log.error();
                myClassService.updateStatus(myclass.getId(), Status.FAILED);
            })
            .onSuccess((o) - > {
                MyClass updatedMyClass = myClassService.updateStatus(myclass.getId(), GameStatus.ENDED);
                streamingService.storeData(updateMyClass);
                dispatching.remove(myclass.getPlatformId());
                //TODO log.info()
            })
            .runAsync(() - > {
                MyClass updatedMyClass =
                myClassService.updateStatus(myclass.getId(), Status.STREAMING);
                streamingService.polling(StreamedClass.builder().myclass(updatedMyClass).build());
                // TODO log.info()
            });
    }
}

这是我使用 Mockito 进行的 JUnit 测试:

@ExtendWith(MockitoExtension.class)
public class DispatcherTaskTest {

    @Mock private MyClassService myClassService;
    @Mock private StreamingService streamingService;
    @InjectMocks private DispatcherTask dispatcherTask;

    @Test
    void test() throws InterruptedException {
        //given
        MyClass myclass = new MyClass().setId(1L).setName("name").setStatus(Status.CREATED);
        when(myClassService.updateStatus(myclass.getId(), Status.STREAMING)).thenReturn(myclass);
        when(myClassService.updateStatus(myclass.getId(), Status.ENDED)).thenReturn(myclass);
        doNothing().when(streamingService).polling(any());
        doNothing().when(streamingService).storeData(any());
        //when
        dispatcherTask.initMethod(myclass)
        //then
        verify(myClassService, times(1)).updateStatus(myclass.getId(), Status.STREAMING);
        verify(myClassService, times(1)).updateStatus(myclass.getId(), Status.ENDED);
    }
}

如果我这样运行它,检查状态 ENDED 的最后验证失败。如果我添加 Thread.sleep(3l); 它会通过。有没有更好或更安全的方法来通过测试而不添加 sleep() 方法?

最佳答案

两个可行的选择

1 - 在 Dispatcher 中添加 shutdownawait 方法

您可以在 Dispatcher 类中添加 shutdownawaitTermination 方法。 (如果需要,您可以统一它们)

public class DispatcherTask 
{    
  //...
  public void shutdownExecutor()
  {
     executor.shutdown();
  }
  public void waitToFinish(long seconds)
  {
     executor.awaitTermination(seconds,TimeUnit.SECONDS);
  }
  //...
}

boolean awaitTermination

Blocks until all tasks have completed execution after a shutdown request, or the timeout occurs, or the current thread is interrupted, whichever happens first.


在您的 Dispatcher 中使用此方法,可以更改测试,以便您可以:

@Test
void test() throws InterruptedException {
    //...
    dispatcherTask.initMethod(myclass);
    verify(myClassService, times(1)).updateStatus(myclass.getId(), Status.STREAMING);
   
    dispatcherTask.shutdownExecutor();
    dispatcherTask.waitToFinish(3L); //this will block without manually sleeping
    verify(myClassService, times(1)).updateStatus(myclass.getId(), Status.ENDED);
}

2 - 添加一个getExecutor 方法并在测试中调用shutdown/await

public class DispatcherTask 
{    
  //...
  public ExecutorService getExecutor()
  {
     return executor;
  }
  //...
}

在你的测试中:

@Test
void test() throws InterruptedException {
    //...
     dispatcherTask.initMethod(myclass);
     verify(myClassService, times(1)).updateStatus(myclass.getId(), Status.STREAMING);
    
     //create a variable in the test or just invoke getExecutor
     dispatcherTask.getExecutor().shutdown();
     dispatcherTask.getExecutor().awaitTermination(3L, TimeUnit.SECONDS); 
     verify(myClassService, times(1)).updateStatus(myclass.getId(), Status.ENDED);
}

关于java - 如何使用 mockito 测试运行异步线程的方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66692132/

相关文章:

java - 在 Java 中创建仅提取一个字段的新列表

java - 如何在java中的junit easymock中编写void方法的测试方法?

java - 模拟我正在测试的同一个类中的方法调用,它真的是代码味道吗?

java - 找不到类 : Empty Test Suite in IntelliJ

java - 如果在多个属性文件中定义了一个属性,Spring 如何选择要使用的属性值?

java - 我们如何在intellij idea项目中找到孤立的 jar

java - 未找到 ManagedExecutorService

C# 多线程

java - 如何在后台线程中启动服务器并知道服务器在启动时没有抛出异常?

java - 创建不同的 ImageView 对象 - 不同的时间