Failed to instantiate Pageable: Specified class is an interface

Al añadir los parámetros de paginación a un controlador de spring. Unos tests que estaba haciendo con MockMvc y Mockito me comenzaron a fallar con el siguiente error:

Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.data.domain.Pageable]: Specified class is an interface

La solución consiste en proporcionar una clase que resuelva los argumentos en tiempo de test (setCustomArgumentResolvers). Esta clase solo atiende al parámetro de tipo Pageable.

public class TestUtils {
    public static MockMvcBuilder prepareMockMvcForPageableArguments(Object... controllers) {
        return MockMvcBuilders.standaloneSetup(controllers)
                .setCustomArgumentResolvers(
                        new HandlerMethodArgumentResolver() {
                            @Override
                            public boolean supportsParameter(MethodParameter parameter) {
                                return parameter.getParameterType().equals(Pageable.class);
                            }

                            @Override
                            public Object resolveArgument(MethodParameter parameter,
                                    ModelAndViewContainer container,
                                    NativeWebRequest request,
                                    WebDataBinderFactory binderFactory) throws Exception {
                                return new PageRequest(0, 50);
                            }
                        });

    }
}

Para utilizarlo:

MockMvc mvc = TestUtils.prepareMockMvcForPageableArguments(controller).build();

Fuente:

https://github.com/terasolunaorg/terasoluna-tourreservation/blob/master/terasoluna-tourreservation-web/src/test/java/org/terasoluna/tourreservation/app/searchtour/SearchTourControllerTest.java

Paginación con Spring Data

A modo de recordatorio.

  • El controlador debe recibir un objeto de tipo Pageable
@GetMapping("/myentities")
public Page getMyEntities(final String userUuid, Pageable pageable) {
    return myRepository.findAllByUserUuid(userUuid, pageable);
}
  • Esto significa que a la llamada al controlador se le pueden pasar los parámetros page, limit y sort para controlar la página que queremos recuperar y el orden.
page=número de página a mostrar
limit=número de elementos a mostrar por página
sort=campo por el que se quiere ordenar
  •  Si se quiere personalizar el nombre de estos parámetros, se puede hacer modificando las siguientes propiedades en el fichero application.properties.
spring.data.rest.page-param-name=page
spring.data.rest.limit-param-name=limit
spring.data.rest.sort-param-name=sort
  • El repositorio tiene que extender el interfaz de Spring PagingAndSortingRepository. JpaRepository ya extiende de este.
  • El método findXX del repositorio debe devolver un objeto de tipo Page y recibir un parámetro de tipo Pageable.
@Repository
public interface MyRepository extends JpaRepository<MyEntity, Long> {
    public Page findAllByUserUuid(String userUuid, Pageable pageable);
}

Referencias

https://docs.spring.io/spring-data/rest/docs/1.1.x/reference/html/paging-chapter.html

Saludos