2016-08-18 367 views
4

使用mockito模拟异步(@Async)方法的最佳方法是什么?以下提供的服务:如何在使用Mockito的Spring Boot中模拟异步(@Async)方法?

@Service 
@Transactional(readOnly=true) 
public class TaskService { 
    @Async 
    @Transactional(readOnly = false) 
    public void createTask(TaskResource taskResource, UUID linkId) { 
     // do some heavy task 
    } 
} 

的Mockito的验证如下:

@RunWith(SpringRunner.class) 
@WebMvcTest(SomeController.class) 
public class SomeControllerTest { 
    @Autowired 
    MockMvc mockMvc; 
    @MockBean  
    private TaskService taskService; 
    @Rule 
    public MockitoRule mockitoRule = MockitoJUnit.rule(); 

    // other details omitted... 

    @Test 
    public void shouldVerify() { 
     // use mockmvc to fire to some controller which in turn call taskService.createTask 
     // .... details omitted 
     verify(taskService, times(1)) // taskService is mocked object 
      .createTask(any(TaskResource.class), any(UUID.class)); 
    } 
} 

上述试验方法shouldVerify总会抛出:

org.mockito.exceptions.misusing.InvalidUseOfMatchersException: 
Misplaced argument matcher detected here: 

-> at SomeTest.java:77) // details omitted 
-> at SomeTest.java:77) // details omitted 

You cannot use argument matchers outside of verification or stubbing. 
Examples of correct usage of argument matchers: 
    when(mock.get(anyInt())).thenReturn(null); 
    doThrow(new RuntimeException()).when(mock).someVoidMethod(anyObject()); 
    verify(mock).someMethod(contains("foo")) 

Also, this error might show up because you use argument matchers with methods that cannot be mocked. 
Following methods *cannot* be stubbed/verified: final/private/equals()/hashCode(). 
Mocking methods declared on non-public parent classes is not supported. 

如果我删除@Async上述异常不会happend从TaskService.createTask方法。

春天启动的版本:1.4.0.RELEASE

版本的Mockito:19年1月10日

+0

您能否提供您的测试的整个实施。具有方法createTask的类是否是超级类? – marians27

+0

嗨Marians27,我编辑了这个问题。重要的一点是,如果我从TaskService类中删除@Async注释,则不会发生异常。 –

+1

'@Async'注释是否会导致aspectj或其他东西被踢入?你确定,你试图验证的'taskService'实例没有被任何方面或类似的东西修改吗? – SpaceTrucker

回答

3

发现,通过改变异步模式AspectJ的固定问题:

@EnableCaching 
@SpringBootConfiguration 
@EnableAutoConfiguration 
@ComponentScan(lazyInit = true) 
@EnableAsync(mode = AdviceMode.ASPECTJ) // Changes here!!! 
public class Main { 
    public static void main(String[] args) { 
     new SpringApplicationBuilder().sources(Main.class) 
            .run(args); 
    } 
} 

我会接受这是一个临时hackish的解决方案,直到我明白了什么是这个问题的真正根源。

3

有一个bug in Spring Boot,我们希望在1.4.1修复。问题是你的模拟TaskService仍然被异步调用,这打破了Mockito。

你可以通过为TaskService创建一个接口并创建一个模拟器来解决这个问题。只要您在实施时离开@Async注释,事情就会起作用。

事情是这样的:

public interface TaskService { 

    void createTask(TaskResource taskResource, UUID linkId); 

} 

@Service 
@Transactional(readOnly=true) 
public class AsyncTaskService implements TaskService { 

    @Async 
    @Transactional(readOnly = false) 
    @Override 
    public void createTask(TaskResource taskResource, UUID linkId) { 
     // do some heavy task 
    } 

} 
+0

奇怪的是,今天刚刚发现,将异步模式更改为AspectJ解决了我的问题:'@EnableAsync(mode = AdviceMode.ASPECTJ)'。见下面的答案。 –

相关问题