2016-08-27 4 views
2

단위 테스트를 처음 사용했습니다. 스프링 부트 애플리케이션 컨트롤러에 대한 테스트를하려고합니다. 그러나 테스트에서 내 모델 속성이나 그런 것을 찾을 수 없습니다. 아래에서 내 코드를 찾고 내가 잘못하고있는 것을 발견하는 데 도움이되기를 바랍니다. 미리 감사드립니다!스프링 부트 컨트롤러의 테스트 모델 속성

실패 스택 추적 :

> java.lang.AssertionError: Model attribute 'restaurants' 
Expected: a collection with size <2> 
    but: collection size was <0> 
    at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:20) 
    at org.springframework.test.web.servlet.result.ModelResultMatchers$1.match(ModelResultMatchers.java:58) 
    at org.springframework.test.web.servlet.MockMvc$1.andExpect(MockMvc.java:171) 
    at com.matmr.restaurantpoll.controller.RestaurantControllerTest.should_search(RestaurantControllerTest.java:79) 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
    at java.lang.reflect.Method.invoke(Method.java:497) 
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) 
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) 
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) 
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) 
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26) 
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75) 
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86) 
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84) 
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) 
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252) 
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94) 
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) 
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) 
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) 
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) 
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) 
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) 
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70) 
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363) 
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191) 
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86) 
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) 
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459) 
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675) 
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382) 
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192) 

RestaurantControllerTest.class

package com.matmr.restaurantpoll.controller; 

import static org.hamcrest.Matchers.allOf; 
import static org.hamcrest.Matchers.hasItem; 
import static org.hamcrest.Matchers.hasProperty; 
import static org.hamcrest.Matchers.is; 
import static org.mockito.Mockito.times; 
import static org.mockito.Mockito.verify; 
import static org.mockito.Mockito.when; 
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; 
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.model; 
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; 
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view; 
import static org.hamcrest.Matchers.*; 
import static org.mockito.Mockito.*; 

import java.util.Arrays; 

import org.junit.Before; 
import org.junit.Test; 
import org.junit.runner.RunWith; 
import org.mockito.InjectMocks; 
import org.mockito.Mock; 
import org.mockito.MockitoAnnotations; 
import org.springframework.boot.test.context.SpringBootTest; 
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 
import org.springframework.test.context.web.WebAppConfiguration; 
import org.springframework.test.web.servlet.MockMvc; 
import org.springframework.test.web.servlet.setup.MockMvcBuilders; 

import com.matmr.restaurantpoll.model.Category; 
import com.matmr.restaurantpoll.model.Restaurant; 
import com.matmr.restaurantpoll.model.filter.RestaurantFilter; 
import com.matmr.restaurantpoll.service.RestaurantService; 

@RunWith(SpringJUnit4ClassRunner.class) 
@SpringBootTest 
@WebAppConfiguration 
public class RestaurantControllerTest { 

    @Mock 
    private RestaurantService restaurantService; 

    @InjectMocks 
    private RestaurantController restaurantController; 

    private MockMvc mockMvc; 

    @Before 
    public void setup() { 
     MockitoAnnotations.initMocks(this); 
     this.mockMvc = MockMvcBuilders.standaloneSetup(restaurantController).setRemoveSemicolonContent(false).build(); 

    } 

    @Test 
    public void should_search() throws Exception { 

     RestaurantFilter filter = new RestaurantFilter(); 
     filter.setName(null); 

     Restaurant first = new RestaurantBuilder() 
       .id(1L) 
       .name("Abra") 
       .description("lots of food") 
       .category(Category.ITALIAN).build(); 

     Restaurant second = new RestaurantBuilder() 
       .id(2L) 
       .name("Kadabra") 
       .description("food for days") 
       .category(Category.PIZZA).build(); 

     when(restaurantService.findByNameIgnoreCaseContaining(filter)).thenReturn(Arrays.asList(first, second)); 

     this.mockMvc.perform(get("/restaurants")) 
      .andExpect(status().isOk()) 
      .andExpect(view().name("restaurantList")) 
      .andExpect(model().attribute("restaurants", hasSize(2))) 
      .andExpect(model().attribute("restaurants", 
        hasItem(allOf(
          hasProperty("id", is(1L)), 
          hasProperty("name", is("Abra")), 
          hasProperty("description", is("lots of food")) 
          )))) 
      .andExpect(model().attribute("restaurants", 
        hasItem(allOf(
          hasProperty("id", is(2L)), 
          hasProperty("name", is("Kadabra")), 
          hasProperty("description", is("food for days")) 
          )))); 

     verify(restaurantService, times(1)).findByNameIgnoreCaseContaining(filter); 
     verifyNoMoreInteractions(restaurantService); 

    } 

} 

RestaurantController.class

package com.matmr.restaurantpoll.controller; 

import java.util.List; 

import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.stereotype.Controller; 
import org.springframework.web.bind.annotation.ModelAttribute; 
import org.springframework.web.bind.annotation.RequestMapping; 
import org.springframework.web.servlet.ModelAndView; 

import com.matmr.restaurantpoll.model.Restaurant; 
import com.matmr.restaurantpoll.model.filter.RestaurantFilter; 
import com.matmr.restaurantpoll.service.RestaurantService; 

@Controller 
@RequestMapping("/restaurants") 
public class RestaurantController { 

    @Autowired 
    private RestaurantService restaurantService; 

    @RequestMapping 
    public ModelAndView pesquisar(@ModelAttribute("filtro") RestaurantFilter filter) { 

     List<Restaurant> filterRestaurants = restaurantService.findByNameIgnoreCaseContaining(filter); 
     ModelAndView mv = new ModelAndView("restaurantList"); 
     mv.addObject("restaurants", filterRestaurants); 

     return mv; 
    } 

} 

RestaurantList.html

<!DOCTYPE html> 
<html xmlns="http://www.w3.org/1999/xhtml" 
    xmlns:th="http://www.thymeleaf.org" 
    xmlns:layout="http://ultraq.net.nz/thymeleaf/layout" 
    layout:decorator="layout"> 
<head> 
<title>Pesquisa de Restaurantes</title> 
</head> 

<section layout:fragment="conteudo"> 

    <div layout:include="MensagemGeral"></div> 

    <div class="panel panel-default"> 
     <div class="panel-heading"> 
      <div class="clearfix"> 
       <h1 class="panel-title liberty-title-panel">Pesquisa de 
        Restaurantes</h1> 
       <a class="btn btn-link liberty-link-panel" 
        th:href="@{/titulos/novo}">Cadastrar Novo Restaurante</a> 
      </div> 
     </div> 

     <div class="panel-body"> 



      <div class="table-responsive"> 
       <table class="table table-bordered table-striped"> 
        <thead> 
         <tr> 
          <th class="text-center col-md-1">#</th> 
          <th class="text-left col-md-2">Nome</th> 
          <th class="text-left col-md-3">Descrição</th> 
          <th class="text-left col-md-2">Categoria</th> 
          <th class="col-md-1"></th> 
         </tr> 
        </thead> 
        <tbody> 
         <tr th:each="restaurant : ${restaurants}"> 

          <td class="text-center" th:text="${restaurant.id}"></td> 

          <td class="text-center" th:text="${restaurant.name}"></td> 

          <td th:text="${restaurant.description}"></td> 

          <td th:text="${restaurant.category.description}"></td> 

          <td class="text-center"><a class="btn btn-link btn-xs" 
           th:href="@{/restaurants/{id}(id=${restaurant.id})}" 
           title="Editar" rel="tooltip" data-placement="top"> <span 
            class="glyphicon glyphicon-pencil"></span> 
          </a> <a class="btn btn-link btn-xs" data-toggle="modal" 
           data-target="#confirmRemove" 
           th:attr="data-id=${restaurant.id}, data-name=${restaurant.name}" 
           title="Excluir" rel="tooltip" data-placement="top"> <span 
            class="glyphicon glyphicon-remove"></span> 
          </a></td> 
         </tr> 
         <tr> 
          <td colspan="6" th:if="${#lists.isEmpty(restaurants)}">Nenhum 
           restaurante foi encontrado!</td> 
         </tr> 
        </tbody> 

       </table> 

      </div> 
     </div> 

     <div layout:include="confirmRemove"></div> 

    </div> 
</section> 
</html> 

답변

1

먼저 테스트 클래스에서 주석을 제거해야합니다. 주석은 통합 테스트에 사용되며 테스트가 느려지 게됩니다.

지금까지 문제가가는대로, 내 생각 엔 당신의 RestaurantFilter 당신이

when(restaurantService.findByNameIgnoreCaseContaining(filter)) 
    .thenReturn(Arrays.asList(first, second)); 

Mockito에서 사용할 때 아무튼 그래서 equalshashCode 그래서 실제로 모의에 인수 일치하지 않는 구현하지 않는다는 것입니다 당신이 준 배열을 반환하지 않습니다. 당신은 equalshashCode를 구현해야 하나 또는, 빠르지 만 가능성이 더 오류가 발생하기 쉬운으로 when(...) 정의를 대체 :

when(restaurantService.findByNameIgnoreCaseContaining(refEq(filter))) 
    .thenReturn(Arrays.asList(first, second)); 

Matchers.refEq이 반사를 사용하여 값을 비교하고 .equals 비교에 의존하지 않습니다.

+0

대단히 감사합니다. 해시 코드와 같음을 추가하면 효과가있었습니다. 주석을 제거하는 것에 대해 클래스 이름 위에있는 주석을 모두 제거해야합니까? –

+0

내 기쁨. 네, 모두 세 개를 제거하십시오. –

관련 문제