Как выполнить модульное тестирование контроллера Spring MVC с помощью @PathVariable? - PullRequest
52 голосов
/ 09 сентября 2009

У меня есть простой аннотированный контроллер, похожий на этот:

@Controller
public class MyController {
  @RequestMapping("/{id}.html")
  public String doSomething(@PathVariable String id, Model model) {
    // do something
    return "view";
  }
}

и я хочу проверить его с помощью модульного теста, подобного этому:

public class MyControllerTest {
  @Test
  public void test() {
    MockHttpServletRequest request = new MockHttpServletRequest();
    request.setRequestURI("/test.html");
    new AnnotationMethodHandlerAdapter()
      .handle(request, new MockHttpServletResponse(), new MyController());
    // assert something
  }
}

Проблема в том, что метод AnnotationMethodHandlerAdapter.handler () генерирует исключение:

java.lang.IllegalStateException: Could not find @PathVariable [id] in @RequestMapping
at org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter$ServletHandlerMethodInvoker.resolvePathVariable(AnnotationMethodHandlerAdapter.java:642)
at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.resolvePathVariable(HandlerMethodInvoker.java:514)
at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.resolveHandlerArguments(HandlerMethodInvoker.java:262)
at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.invokeHandlerMethod(HandlerMethodInvoker.java:146)

Ответы [ 7 ]

46 голосов
/ 16 марта 2010

Я бы назвал то, что вы хотите, после интеграционного теста на основе терминологии в справочном руководстве Spring. Как насчет того, чтобы сделать что-то вроде:

import static org.springframework.test.web.ModelAndViewAssert.*;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({/* include live config here
    e.g. "file:web/WEB-INF/application-context.xml",
    "file:web/WEB-INF/dispatcher-servlet.xml" */})
public class MyControllerIntegrationTest {

    @Inject
    private ApplicationContext applicationContext;

    private MockHttpServletRequest request;
    private MockHttpServletResponse response;
    private HandlerAdapter handlerAdapter;
    private MyController controller;

    @Before
    public void setUp() {
       request = new MockHttpServletRequest();
       response = new MockHttpServletResponse();
       handlerAdapter = applicationContext.getBean(HandlerAdapter.class);
       // I could get the controller from the context here
       controller = new MyController();
    }

    @Test
    public void testDoSomething() throws Exception {
       request.setRequestURI("/test.html");
       final ModelAndView mav = handlerAdapter.handle(request, response, 
           controller);
       assertViewName(mav, "view");
       // assert something
    }
}

Для получения дополнительной информации я написал запись в блоге об интеграционном тестировании аннотаций Spring MVC .

37 голосов
/ 27 февраля 2013

Начиная с весны 3.2, существует правильный способ проверить это элегантным и простым способом. Вы сможете делать такие вещи:

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration("servlet-context.xml")
public class SampleTests {

  @Autowired
  private WebApplicationContext wac;

  private MockMvc mockMvc;

  @Before
  public void setup() {
    this.mockMvc = webAppContextSetup(this.wac).build();
  }

  @Test
  public void getFoo() throws Exception {
    this.mockMvc.perform(get("/foo").accept("application/json"))
        .andExpect(status().isOk())
        .andExpect(content().mimeType("application/json"))
        .andExpect(jsonPath("$.name").value("Lee"));
  }
}

Для получения дополнительной информации, посмотрите на http://blog.springsource.org/2012/11/12/spring-framework-3-2-rc1-spring-mvc-test-framework/

10 голосов
/ 10 августа 2011

Многообещающий фреймворк для тестирования Spring MVC https://github.com/SpringSource/spring-test-mvc

3 голосов
/ 27 марта 2013

При условии, что вы используете Spring 3.0.x.

Здесь я предлагаю объединить ответы Эмиля и scarba05, используя spring-test, а не spring-test-mvc. Пожалуйста, пропустите этот ответ и обратитесь к примерам spring-test-mvc, если вы используете Spring 3.2.x или выше

MyControllerWithParameter.java

@Controller
public class MyControllerWithParameter {
@RequestMapping("/testUrl/{pathVar}/some.html")
public String passOnePathVar(@PathVariable String pathVar, ModelMap model){
    model.addAttribute("SomeModelAttribute",pathVar);
    return "viewName";
}
}

MyControllerTest.java

import static org.springframework.test.web.ModelAndViewAssert.assertViewName;
import java.util.HashMap;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.web.ModelAndViewAssert;
import org.springframework.web.servlet.HandlerAdapter;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = 
    {"file:src\\main\\webapp\\WEB-INF\\spring\\services\\servlet-context.xml" 
    })
public class MyControllerTest {

private MockHttpServletRequest request;
private MockHttpServletResponse response;
private HandlerAdapter handlerAdapter;

@Before
public void setUp() throws Exception {
    request = new MockHttpServletRequest();
    response = new MockHttpServletResponse();
    this.handlerAdapter = applicationContext.getBean(AnnotationMethodHandlerAdapter.class);
}

//  Container beans
private MyControllerWithParameter myController;
private ApplicationContext applicationContext;
public ApplicationContext getApplicationContext() {
    return applicationContext;
}
@Autowired
public void setApplicationContext(ApplicationContext applicationContext) {
    this.applicationContext = applicationContext;
}
public MyControllerWithParameter getMyController() {
    return myController;
}
@Autowired
public void setMyController(MyControllerWithParameter myController) {
    this.myController = myController;
}

@Test
public void test() throws Exception {
    request.setRequestURI("/testUrl/Irrelavant_Value/some.html");
    HashMap<String, String> pathvars = new HashMap<String, String>();
    // Populate the pathVariable-value pair in a local map
    pathvars.put("pathVar", "Path_Var_Value");
    // Assign the local map to the request attribute concerned with the handler mapping 
    request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, pathvars);

    final ModelAndView modelAndView = this.handlerAdapter.handle(request, response, myController);

    ModelAndViewAssert.assertAndReturnModelAttributeOfType(modelAndView, "SomeModelAttribute", String.class);
    ModelAndViewAssert.assertModelAttributeValue(modelAndView, "SomeModelAttribute", "Path_Var_Value");
    ModelAndViewAssert.assertViewName(modelAndView, "viewName");
}

}

3 голосов
/ 09 сентября 2009

Сообщение об исключении относится к переменной «feed», которой нет в вашем примере кода, скорее всего, она вызвана чем-то, что вы нам не показали.

Кроме того, ваш тест проверяет Spring и ваш собственный код. Это действительно то, что вы хотите сделать?

Лучше предположить, что Spring работает (что он делает), и просто протестировать свой собственный класс, то есть напрямую вызвать MyController.doSomething(). Это одно из преимуществ подхода аннотаций - вам не нужно использовать фиктивные запросы и ответы, вы просто используете доменные POJO.

2 голосов
/ 08 апреля 2010

Я обнаружил, что вы можете вручную вставить отображение PathVariable в объект запроса. Это явно не идеально, но, похоже, работает. В вашем примере что-то вроде:

@Test
public void test() {
    MockHttpServletRequest request = new MockHttpServletRequest();
    request.setRequestURI("/test.html");
    HashMap<String, String> pathvars = new HashMap<String, String>();
    pathvars.put("id", "test");
    request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, pathvars);
    new AnnotationMethodHandlerAdapter().handle(request, new MockHttpServletResponse(), new MyController());
   // assert something
}

Мне определенно было бы интересно найти лучший вариант.

1 голос
/ 29 июля 2010

Я не уверен, что мой оригинальный ответ поможет с @PathVariable. Я только что попробовал протестировать @PathVariable и получаю следующее исключение:

org.springframework.web.bind.annotation.support.HandlerMethodInvocationException: не удалось вызвать метод-обработчик [public org.springframework.web.servlet.ModelAndView test.MyClass.myMethod (test.SomeType); вложенное исключение - java.lang.IllegalStateException: не удалось найти @PathVariable [имя_параметра] в @ RequestMapping

Причина в том, что переменные пути в запросе анализируются перехватчиком. У меня работает следующий подход:

import static org.springframework.test.web.ModelAndViewAssert.*;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"file:web/WEB-INF/application-context.xml",
        "file:web/WEB-INF/dispatcher-servlet.xml"})    
public class MyControllerIntegrationTest {

    @Inject
    private ApplicationContext applicationContext;

    private MockHttpServletRequest request;
    private MockHttpServletResponse response;
    private HandlerAdapter handlerAdapter;

    @Before
    public void setUp() throws Exception {
        this.request = new MockHttpServletRequest();
        this.response = new MockHttpServletResponse();

        this.handlerAdapter = applicationContext.getBean(HandlerAdapter.class);
    }

    ModelAndView handle(HttpServletRequest request, HttpServletResponse response)
            throws Exception {
        final HandlerMapping handlerMapping = applicationContext.getBean(HandlerMapping.class);
        final HandlerExecutionChain handler = handlerMapping.getHandler(request);
        assertNotNull("No handler found for request, check you request mapping", handler);

        final Object controller = handler.getHandler();
        // if you want to override any injected attributes do it here

        final HandlerInterceptor[] interceptors =
            handlerMapping.getHandler(request).getInterceptors();
        for (HandlerInterceptor interceptor : interceptors) {
            final boolean carryOn = interceptor.preHandle(request, response, controller);
            if (!carryOn) {
                return null;
            }
        }

        final ModelAndView mav = handlerAdapter.handle(request, response, controller);
        return mav;
    }

    @Test
    public void testDoSomething() throws Exception {
        request.setRequestURI("/test.html");
        request.setMethod("GET");
        final ModelAndView mav = handle(request, response);
        assertViewName(mav, "view");
        // assert something else
    }

Я добавил новое сообщение в блоге по аннотации mvc весеннего тестирования интеграции

...