Mockito失败:实际上,这个模拟没有互动

时间:2017-07-17 15:50:12

标签: spring-mvc junit mockito spring-test spring-restcontroller

我正在尝试使用JUnit,Mockito,Spring测试和Spring Security测试来测试Spring rest控制器类。以下是我正在进行测试的其他控制器类;

<ion-header>
    <ion-navbar color="secondary">
        <ion-title>
            CEPs!
        </ion-title>
    </ion-navbar>
</ion-header>

<ion-content margin>
    <ion-item ng-show="false">{{ item.cep + "\n" + item.logradouro + "\n" + item.complemento + "\n" + item.bairro + "\n" + item.localidade + "\n" +
        item.uf}}
    </ion-item>

    <form margin-top (ngSubmit)="test()">
        <ion-item>
            <ion-input type="number" max="99999999" [(ngModel)]="form.cep" name="form"></ion-input>
        </ion-item>
        <button ion-button type="submit" center>Pesquisar CEP</button>
    </form>
</ion-content>

以下是我的测试类;

@RestController
public class EmployeeRestController {

    @Autowired
    private EmployeeService employeeService;

    @PreAuthorize("hasAnyRole('ROLE_EMPSUPEADM')")
    @RequestMapping(value = "/fetch-timezones", method = RequestMethod.GET)
    public ResponseEntity<List<ResponseModel>> fetchTimeZones() {
        List<ResponseModel> timezones = employeeService.fetchTimeZones();
        return new ResponseEntity<>(timezones, HttpStatus.OK);
    }

}

}

我通过引用很多教程来完成上面的测试课程。问题是我无法清楚地理解一切。所以,我有以下疑惑。

  1. 我正在创建一个EmployeeService的模拟并使用@InjectMocks将其注入EmployeeRestController,那么为什么我会遇到以下失败;

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = {SpringConfiguration.class})
    @WebAppConfiguration
    public class EmployeeRestControllerUnitTest {
    
    private MockMvc mockMvc;
    
    @Autowired
    private WebApplicationContext webApplicationContext;
    
    @Mock
    private EmployeeService employeeService;
    
    @InjectMocks
    private EmployeeRestController employeeRestController;
    
    @Before
    public void init() {
        MockitoAnnotations.initMocks(this);
        Mockito.reset(employeeService);
        mockMvc = MockMvcBuilders
                    .webAppContextSetup(webApplicationContext)
                    .build();
    }
    
    @Test
    @WithMockUser(roles = {"EMPSUPEADM"})
    public void testFetchTimezones() {
        try {
            mockMvc.perform(get("/fetch-timezones"))
              .andExpect(status().isOk())               
              .andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
              .andExpect(jsonPath("$", hasSize(4)));
            verify(emploeeService, times(1)).fetchTimeZones();
            verifyNoMoreInteractions(employeeService);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
  2. MockMvcBuilders.webAppContextSetup(webApplicationContext).build();完全有效。

  3. 我知道MockMvcBuilders.standaloneSetup(employeeRestController)用于测试单个控制器类,弹出配置将不适用于此方法。我们怎样才能为这种方法提供弹簧配置,是否可能。

  4. 最后,这段代码如何:Mockito.reset(employeeService);作品。

1 个答案:

答案 0 :(得分:1)

1)你确认

verify(emploeeService, times(1)).fetchTimeZones();

但是你没有为它设置模拟行为(在你调用mockMvc.perform(get("/fetch-timezones"))之前)。

List<ResponseModel> timezones = new ArrayList<>();
when(emploeeService.fetchTimeZones()).thenReturn(timezones );

2)从上下文创建MockMvc。 mockmvc模拟Web容器,尽可能使用mock,但支持http调用并且可以调用控制器。

  

它代表Dispatcher Servlet和所有必需的MVC组件,   允许我们在适当的Web环境中测试端点,但是   没有运行服务器的开销。

3)当你使用:

@MockBean private EmployeeService employeeService;

你覆盖真实服务。从测试类中删除它将在测试中使用实际服务。而不是使用@Mock使用@MockBean。 @MockBean它被容器覆盖,使用@Mock你需要通过反射将它注入控制器

或没有带反射的弹簧靴:

@Before
public void init() {
    MockitoAnnotations.initMocks(this);
    Mockito.reset(employeeService);
    mockMvc = MockMvcBuilders
                .webAppContextSetup(webApplicationContext)
                .build();
    EmployeeRestController employeeRestController=  
            webAppContext.getBean(EmployeeRestController.class);
    ReflectionTestUtils.setField(employeeRestController, 
                                 "employeeService",
                                 employeeService);
}

4)Mockito.reset(employeeService); - 您重置之前设置的所有行为。模拟包含来自when(), verify()的信息并控制它,调用重置 - 它是干净的所有信息。