【问题标题】:How to mock Asynchronous (@Async) method in Spring Boot using Mockito?如何使用 Mockito 在 Spring Boot 中模拟异步(@Async)方法?
【发布时间】:2016-08-18 10:42:05
【问题描述】:

用 mockito 模拟异步 (@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.

如果我从TaskService.createTask 方法中删除@Async,则不会发生上述异常。

Spring Boot 版本:1.4.0.RELEASE

Mockito 版本:1.10.19

【问题讨论】:

  • 你能提供你的测试的整个实现吗?有方法 createTask 的类是超类吗?
  • 嗨,Marians27,我已经编辑了这个问题。重要的一点是,如果我从 TaskService 类中删除 @Async 注释,则不会发生异常。
  • @Async 注释是否会导致 aspectj 或其他东西启动?您确定您尝试验证的 taskService 实例没有被任何方面等修改吗?
  • 嗨 SpaceTrucker,将异步模式更改为 AspectJ 解决了这个问题。我仍然不知道造成这种情况的确切根本原因是什么。

标签: java spring asynchronous spring-boot mockito


【解决方案1】:

发现通过将异步模式更改为 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);
    }
}

在我了解此问题的真正根本原因之前,我会接受这是一个临时的黑客解决方案。

【讨论】:

    【解决方案2】:

    我们希望在 1.4.1 中修复 bug in Spring Boot。问题是您的模拟 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
        }
    
    }
    

    【讨论】:

    • 奇怪,今天刚刚发现将异步模式更改为 AspectJ 解决了我的问题:@EnableAsync(mode = AdviceMode.ASPECTJ)。请参阅下面的答案。
    • 如果您使用 JDK/CGLIB 代理,请使用此解决方案 stackoverflow.com/questions/30648397/…
    猜你喜欢
    • 2019-05-05
    • 1970-01-01
    • 1970-01-01
    • 2022-01-25
    • 2015-12-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多