2016-08-27 3 views
2

Ich bin neu im Komponententest. Ich versuche, Tests zu einem Spring Boot Application Controller zu machen. Aber der Test kann mein Modellattribut oder etwas ähnliches nicht finden. Unten findest du meinen Code und hilf mir hoffentlich herauszufinden, was ich falsch mache. Danke im Voraus!Testen des Modellattributs in Spring Boot Controllern

Failure Stack-Trace:

> 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> 

Antwort

1

Zunächst sollten Sie die Anmerkungen aus Ihrer Testklasse entfernen, sie werden für Integrationstests verwendet und verlangsamen nur Ihren Test.

Soweit Ihr Problem geht, ist meine Vermutung, dass Ihr RestaurantFilter nicht equals und hashCode so nicht implementiert, wenn Sie es in

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

Mockito verwenden eigentlich nicht mit dem Modell das Argument übereinstimmt, so dass es doesn Gebe das Array, das du ihm gegeben hast, nicht zurück. Sie sollten entweder implementieren equals und hashCode oder schneller, aber möglicherweise mehr fehleranfällig, ersetzen Sie die when(...) Definition mit:

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

Matchers.refEq werden die Werte mit Reflexion vergleichen und beruht nicht auf .equals Vergleich.

+0

Vielen Dank! Das Hinzufügen von Hashcode und Equals hat den Job erledigt. Über das Entfernen von Anmerkungen, sollte ich alle über dem Klassennamen entfernen? –

+0

Mein Vergnügen. Ja, entferne alle drei. –

Verwandte Themen