wake-up-neo.com

Der isolierte Controller-Test kann kein Pageable erzeugen

Ich habe einen Spring MVC Controller, der die Paginierungsunterstützung von Spring-Data verwendet:

@Controller
public class ModelController {

    private static final int DEFAULT_PAGE_SIZE = 50;

    @RequestMapping(value = "/models", method = RequestMethod.GET)
    public Page<Model> showModels(@PageableDefault(size = DEFAULT_PAGE_SIZE) Pageable pageable, @RequestParam(
            required = false) String modelKey) {

//..
        return models;
    }

}

Und ich möchte das RequestMapping mit dem Nice Spring MVC Test Support testen. Um diese Tests schnell und isoliert von all den anderen Dingen zu halten, möchte ich nicht den vollständigen ApplicationContext erstellen:

public class ModelControllerWebTest {
    private MockMvc mockMvc;

    @Before
    public void setup() {
        ModelController controller = new ModelController();
        mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
    }

    @Test
    public void reactsOnGetRequest() throws Exception {
        mockMvc.perform(get("/models")).andExpect(status().isOk());
    }

}

Dieser Ansatz funktioniert gut mit anderen Controllern, die kein Pageable erwarten, aber mit diesem Controller bekomme ich einen dieser Nice Long Spring Stacktraces. Es klagt, dass es nicht möglich sei, Pageable zu instanziieren:

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [org.springframework.data.domain.Pageable]: Specified class is an interface
at   
.... lots more lines

Frage: Wie ändere ich meinen Test, damit die magische Konvertierung von No-Request-Parameter-to-Pageable korrekt ausgeführt wird?

Hinweis: In der eigentlichen Anwendung funktioniert alles einwandfrei.

33
Jens Schauder

Das Problem mit pageable kann gelöst werden, indem ein benutzerdefinierter Argumenthandler bereitgestellt wird. Wenn dies festgelegt ist, werden Sie in einer ViewResolver-Ausnahme (Schleife) ausgeführt. Um dies zu vermeiden, müssen Sie einen ViewResolver (z. B. eine anonyme JSON-ViewResolver-Klasse) festlegen.

mockMvc = MockMvcBuilders.standaloneSetup(controller)
            .setCustomArgumentResolvers(new PageableHandlerMethodArgumentResolver())
            .setViewResolvers(new ViewResolver() {
                @Override
                public View resolveViewName(String viewName, Locale locale) throws Exception {
                    return new MappingJackson2JsonView();
                }
            })
            .build();
40
meistermeier

Fügen Sie einfach @EnableSpringDataWebSupport für den Test hinzu. Das ist es.

33
user2031839

Für Spring Boot einfach die für mich gelösten ArgumentsResolvers hinzufügen: 

Aus dem Code, der den Fehler ausgelöst hat: 

this.mockMvc = MockMvcBuilders.standaloneSetup(weightGoalResource).build();

Zu diesem, was funktioniert: 

this.mockMvc = MockMvcBuilders.standaloneSetup(weightGoalResource)
            .setCustomArgumentResolvers(new PageableHandlerMethodArgumentResolver())
            .build();
3
groo

Stolperte nur darüber.
Ich wollte darauf eine Antwort geben. 

Wenn Sie die Pageable-Funktionalität nicht testen möchten, lassen Sie sie einfach aus.
Getestet am Spring Boot 2.0.6:

Controller-Code:

 @RequestMapping(value = "/searchbydistance/{distance}/{address}/page")
 public String searchByDistance(@PathVariable int distance, 
                                @PathVariable @NonNull String address, 
                                Model model, Pageable pageable, 
                                Locale locale, 
                                RedirectAttributes redirectAttributes) {
 }

Testcode:

 @Test
 @WithMockUser(username = "user", authorities = {"AdminRole"})
 public void testSearchByDistance() {

    try {
        UriComponents uri = UriComponentsBuilder.fromUriString("/joboffers/searchbydistance/{distance}/{address}/page").buildAndExpand(10, "Deggendorf");
        this.mockMvc.perform(get(uri.toUri())).andExpect(status().isOk());
    } catch (Exception e) {
        log.error(e.getMessage());
    }
}

Hoffe das hilft ...
Mit freundlichen Grüßen
Thomas

0
Thomas Lang