单元测试 Spring returns PagedResources 的 MVC 控制器

Unit Testing Spring MVC Controller that returns PagedResources

我正在使用 Spring Boot 1.4.1、Spring Data Jpa 和 Spring Data Test 构建应用程序。

我有以下控制器,我想用它来 return 分页帐户:

@RequestMapping(method=RequestMethod.GET)
public HttpEntity<PagedResources<Account>> getAccounts(
        Pageable pageable, 
        PagedResourcesAssembler assembler,
        @RequestParam(value="name", required = false) String name, 
        @RequestParam(value="username", required = false) String username, 
        @RequestParam(value="email", required = false) String email, 
        @RequestParam(value="lastName", required = false) String lastName,
        @RequestParam(value="size", required = true, defaultValue = "10") Integer size,
        @RequestParam(value="page", required = true, defaultValue = "0") int page,
        @RequestParam(value="sort", required = false, defaultValue = "username") String sort,
        @RequestParam(value="direction", required = false, defaultValue = "asc") String direction,
        UriComponentsBuilder uriBuilder,
        HttpServletRequest request,
        HttpServletResponse response) {

    // form page request
    AccountList list = null;
    Page<Account> resultPage = null;
    Direction sortDirection = Direction.ASC;
    if(direction.equals("desc")) {
        sortDirection = Direction.DESC;
    }
    PageRequest pReq = new PageRequest(page, size, sortDirection, sort);

    resultPage = accountService.findAll(pReq);

    return new ResponseEntity<>(assembler.toResource(resultPage), HttpStatus.OK);
}

我的测试方法是:

@Test
public void getAccountsTest() throws Exception {
    String uri = uriBase + "/accounts";

    List<Account> list = new ArrayList<Account>();
    list.add(accountOne);
    list.add(accountTwo);

    Page<Account> returnPage = new PageImpl<Account>(list, new PageRequest(0,10), list.size());

    when(accountService.findAll(any(PageRequest.class))).thenReturn(returnPage);

    mockMvc.perform(get(uri))
            .andExpect(status().isOk())
            .andDo(print())
            .andExpect(content().contentType(TestUtil.APPLICATION_JSON_UTF8))
            .andExpect(jsonPath("$.accounts", hasSize(2)))
            .andExpect(jsonPath("$.accounts[*].username", 
                    hasItems(endsWith(accountOne.getUsername()), endsWith(accountTwo.getUsername()))))
            .andExpect(jsonPath("$.accounts[*].email", 
                    hasItems(endsWith(accountOne.getEmail()), endsWith(accountTwo.getEmail()))));
}

我的问题是,当我 运行 JUnit 测试时它因错误

而中断
org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.data.domain.Pageable]: Specified class is an interface
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:99)
at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.createAttribute(ModelAttributeMethodProcessor.java:142)
at org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor.createAttribute(ServletModelAttributeMethodProcessor.java:80)
at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.resolveArgument(ModelAttributeMethodProcessor.java:102)
at org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:121)
at org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:161)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:128)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:114)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:622)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
at org.springframework.test.web.servlet.TestDispatcherServlet.service(TestDispatcherServlet.java:65)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
at org.springframework.mock.web.MockFilterChain$ServletFilterProxy.doFilter(MockFilterChain.java:167)
at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:134)
at org.springframework.test.web.servlet.MockMvc.perform(MockMvc.java:155)
at de.campuz.platform.rest.controller.AccountControllerTestDoc.getAccountsTest(AccountControllerTestDoc.java:93)
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:498)
at org.junit.runners.model.FrameworkMethod.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.restdocs.JUnitRestDocumentation.evaluate(JUnitRestDocumentation.java:55)
at org.junit.rules.RunRules.evaluate(RunRules.java:20)
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.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access[=14=]0(ParentRunner.java:58)
at org.junit.runners.ParentRunner.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)

我在我的网络配置中添加了@EnableSpringDataWebSupport 注释,但错误仍然存​​在。当 运行 将应用程序作为控制器正常工作时,我将获得分页帐户作为 JSON 响应。但是我不知道如何测试我的控制器。

有没有人知道如何解决这个特定问题或如何对 Spring MVC 控制器进行单元测试,这些控制器通常 return 分页内容?

我通过从 Controller 构造函数中删除 Pageable 分页来解决它,因为我根本不需要它。然后 PagedResourcesAssembler 汇编程序抛出错误

org.springframework.beans.BeanInstantiationException: 
Failed to instantiate [org.springframework.data.web.PagedResourcesAssembler]: 
No default constructor found; nested exception is java.lang.NoSuchMethodException: 
org.springframework.data.web.PagedResourcesAssembler.<init>()

我深入研究了 ResourceAssembler 的主题并提出了以下解决方案:

控制器:

@RequestMapping(method=RequestMethod.GET)
public ResponseEntity<PagedResources<AccountResource>> getAccounts(
        @RequestParam(value="name", required = false) String name, 
        @RequestParam(value="username", required = false) String username, 
        @RequestParam(value="email", required = false) String email, 
        @RequestParam(value="lastName", required = false) String lastName,
        @RequestParam(value="size", required = true, defaultValue = "10") Integer size,
        @RequestParam(value="page", required = true, defaultValue = "0") int page,
        @RequestParam(value="sort", required = false, defaultValue = "username") String sort,
        @RequestParam(value="direction", required = false, defaultValue = "asc") String direction,
        UriComponentsBuilder uriBuilder,
        HttpServletRequest request, 
        HttpServletResponse response) {

    // Build page request
    AccountList list = null;
    Page<Account> resultPage = null;
    Direction sortDirection = Direction.ASC;
    if(direction.equals("desc")) {
        sortDirection = Direction.DESC;
    }
    PageRequest pReq = new PageRequest(page, size, sortDirection, sort);

    resultPage = accountService.findAll(pReq);

    HateoasPageableHandlerMethodArgumentResolver resolver = new HateoasPageableHandlerMethodArgumentResolver();
    PagedResourcesAssembler<Account> accountPageAssembler = new PagedResourcesAssembler<Account>(resolver, null);
    return new ResponseEntity<PagedResources<AccountResource>>(accountPageAssembler.toResource(resultPage, new AccountResourceAsm()), HttpStatus.OK);
}

我在其中使用 HateoasPageableHandlerMethodArgumentResolver 创建资源组装器以将页面转换为我想要的 PagedResource return。

然后我终于可以 运行 控制器测试(如问题中所示)成功。

answer 对我帮助很大,感谢 Oliver Gierke 和 Patrick Cornelissen。

有同样的问题,我想避免"manually"在控制器中获取PagedResourcesAssembler

将此添加到测试中 class 为我修复了它:

@Import(HateoasAwareSpringDataWebConfiguration.class)