使用Spring MVC和模拟服务测试REST服务

时间:2020-10-10 14:20:39

标签: java spring spring-mvc mocking mockito

我有一个Spring MVC应用程序。我要测试此控制器:

@RunWith(SpringRunner.class)
@WebAppConfiguration
@ContextConfiguration(locations = {
        "classpath:testDatabaseContext.xml"
})
public class TimeControllerTests {

    @Autowired
    private WebApplicationContext wac;

    private MockMvc mockMvc;

    @Mock
    private AutorisationService autorisationService;

    @Before
    public void setup() {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
    }
    
    @Test
    public void should_OK() throws Exception {

doReturn(User.builder().build()).when(autorisationService).findById(any(Date.class),anyLong(), 1L);

        mockMvc.perform(get("/time/2")
                .contentType(APPLICATION_JSON))
                .andExpect(status().isOk());
    }
}

但是运行测试时出现此错误:

org.mockito.exceptions.misusing.NullInsteadOfMockException: 
Argument passed to when() is null!
Example of correct stubbing:
    doThrow(new RuntimeException()).when(mock).someMethod();
Also, if you use @Mock annotation don't miss openMocks()

1 个答案:

答案 0 :(得分:1)

我看到了无法正常运行的几种原因:

A-仅靠@Mock不足以初始化模拟。您需要:

  • 在设置中调用MockitoAnnotations.initMocks()
  • 删除@Mock并在设置中调用Mockito.mock(UserRepository.class)

但是我认为这还不够,这使我进入了:

B-您的控制器看起来是由spring管理的,而您模拟的AutorisationService却不受。

我假设您的控制器使用了应该由spring注入的AutorisationService。

您在这里有几种选择:

  • 选项1-完全不使用spring进行测试,而通过mockito管理控制器和服务。 看起来应该像:
@InjectMocks
private SomeController someController;

@Mock
private AutorisationService autorisationService;

@Before
public void setup() {
    MockitoAnnotations.initMocks();
    this.mockMvc = MockMvcBuilders.standaloneSetup(someController).build();
}

请注意standaloneSetup方法:此处的想法是您没有使用spring上下文(然后可以删除顶部的注释)。

  • 选项2:使用不带springboot的spring,并提供模拟服务。

有种骇客,但我经常使用。

您必须提供一个带有AutorisationService类型的bean的自定义spring上下文,但是该类是由嘲讽提供的。

// All all other beans necessary
// You can use whatever you need in your context : @ComponentScan("somepackage"), @Import(Config1.class, Config2.class), @ImportResource("context.xml"), @Bean methods ...
@Configuration
class SpringWithMockitoConfig {
    @Bean
    AutorisationService autorisationService() {
        return Mockito.mock(AutorisationService.class);
    }
}

然后,您的测试将受到两个限制:

  • 导入自定义配置:@ContextConfiguration(classes = SpringWithMockitoConfig.class)
  • 使用设置方法:Mockito.reset(autorisationService);
  • 在每个测试上重置模拟

使用此解决方案,您不需要真正的AutorisationService依赖的bean(我假设是Dao,DataSource等)。

基本上只有mvc部分。

  • 选项3:使用SpringBoot和@MockBean注释-我还不熟悉