Tests de integración con Spring Boot + Spring Security en servicios REST

2
17321

En este tutorial vamos a ver cómo hacer Tests de integración con Spring Boot y Spring Security en servicios REST

0. Índice de contenidos

1. Introducción

Combinando herramientas como spring-boot, spring-security y spring-mvc pueden ofrecernos soluciones muy sencillas de desarrollar y que cubren muchas de las requisitos comunes
de hoy día, como puede ser, aplicar seguridad a la capa de servicios REST de nuestra aplicación (aunque hoy día se está externalizando la seguridad de servicios en herramientas de apoyo, como un API Manager. Más info aquí).

Este tutorial tiene dos objetivos principales:

  • Veremos cómo implementar seguridad en servicios REST a través de configuración Spring basado en Java con Spring Security. La aplicación que contendrá dicho servicio estará desarrollada con spring-boot. Para más información sobre spring-boot, puedes consultar este tutorial
  • Desarrollaremos tests de integración con spring-boot y TestRestTemplate sobre nuestro servicio, y veremos cómo aplicar la seguridad básica en las peticiones de forma sencilla.

2. Entorno

  • Macbook pro core i7 con 16gb RAM
  • SO: Yosemite
  • IDE: IntelliJ IDEA 14
  • Gestión de dependencias: Apache Maven 3.3.3
  • Java: JDK 1.8
  • Spring: 4.1.6.RELEASE
  • Spring-boot: 1.2.5.RELEASE
  • Spring-security: 3.2.2.RELEASE

3. Aplicación spring-boot con Spring security

Lo primero que vamos a hacer es configurar nuestra aplicación con spring-boot, y luego le aplicaremos una capa de seguridad básica con autenticación usuario/contraseña.
Se muestra a continuación el código y luego daremos una explicación de cada elemento. Nuestra aplicación estará configurada en una clase Application.java con el siguiente contenido:

@SpringBootApplication
@EnableAutoConfiguration
@EnableWebSecurity
@ComponentScan("com.tutoriales.springbootit")
public class Application extends WebSecurityConfigurerAdapter {

    @Resource
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().withUser("user").password("pass").roles("USER");
    }

    public static void main(String[] args) {

        SpringApplication.run(Application.class, args);
    }
}

Expliquemos el código por línea:

  • Línea 1. @SpringBootApplication: Esta anotación indica que se trata de una aplicación basada en spring-boot
  • Línea 2. @EnableAutoConfiguration: Anotación de spring-boot que incluye beans recomendados dependiendo de las dependencias que tengas en tu proyecto. Es decir, que si añades una dependencia tomcat-embedded.jar, te creará una instancia de TomcatEmbeddedServletContainerFactory, porque es posible que la uses. Es parte de la «magia» que hace spring-boot por tí
  • Línea 3. @EnableWebSecurity: Anotación de spring-security que indica a la configuración de Spring que se van a redefinir métodos de las clases WebSecurityConfigurer o de WebSecurityConfigurerAdapter (que es la que usaremos en este tutorial)
  • Línea 4. @ComponentScan(..): Anotación de spring para la búsqueda de componentes por paquetes.
  • Línea 5. Nuesta aplicación extiende de WebSecurityConfigurerAdapter para redefinir los métodos de seguridad necesarios. En este caso sólo implementaremos seguridad básica.
  • Línea 8. Se redefine este método para aplicar autenticación básica a nuestra aplicación. Servirá para todas las llamadas HTTP que se realicen sobre el contexto de la misma
  • Línea 12. Este es el main que será ejecutado desde el test de integración

Con esto ya podemos levantar una aplicación bajo un contenedor de servlet (por defecto Tomcat 8), y con seguridad básica usuario/password implementada. Las posibilidades de configuración de seguridad a nivel de aplicación,
mediante el uso de WebSecurityConfigurerAdapter son muy amplias (de hecho puedes configurar la mayoría de aspectos que te proporciona spring-security).

No hemos necesitado hacer uso de spring-security 4 para realizar esta solución. No obstante, spring-security 4 nos ofrece bastantes funcionalidades nuevas muy interesantes. Os dejo el enlace a su documentación (v4.0.1.RELEASE) aquí.

4. Definición del servicio REST

Para esta solución hemos implementado un servicio REST muy sencillo, con un sólo método GET, que obtiene información del bean Persona. Dicho bean lo podemos ver a continuación:


public class Person implements Serializable {

private Long id;

private String name;

private String surname;

private String phoneNumber;

//Métodos getter/setter, equals y hashCode

}

Y el controlador Spring se muestra a continuación:

@RestController
public class PersonRestController {

    private static final Logger LOG = LoggerFactory.getLogger(PersonRestController.class);

    private List<Person> personList = new ArrayList<Person>();

    {
        personList.add(new Person(1L, "Pepe", "Ruiz", "666-555-444"));
        personList.add(new Person(2L, "Paco", "Rodríguez", "633-222-444"));
    }

    @RequestMapping(value = "/person/{id}", method = RequestMethod.GET)
    @ResponseStatus(value = HttpStatus.OK)
    public Person obtainPersonDetails(@AuthenticationPrincipal Principal principal, @PathVariable("id") long id) throws PersonException {

        LOG.info(new StringBuilder("User: ").append(principal.getName()).append(" requesting for person: ").append(id).toString());

        for(Person person : personList) {
            if(person.getId().equals(id)) {
                return person;
            }
        }

        throw new PersonException("Person has not been found!");
    }

    @ExceptionHandler(PersonException.class)
    @ResponseStatus(value = HttpStatus.NOT_FOUND)
    public void personExceptionHandler(final Exception exception) {
        LOG.warn("REST Service exception: " + exception.getMessage());
    }
}

Como podéis ver, se trata de un servicio muy sencillo que almacena una lista de dos personas, simulando información que viene de, por ejemplo, una base de datos.

En el servicio cabe destacar lo siguiente:

  • Línea 1. @RestController: Anotación que combina @Controller con @ResponseBody en los métodos del servicio.
  • Línea 15.@AuthenticationPrincipal: Anotación de spring-security que indica que el parámetro de tipo Principal se inyectará con la información del usuario que hace la petición contra el servicio
  • Línea 30. Manejador de excepción PersonException en caso de lanzar una petición con una persona que no existe

El servicio devolverá un 200(OK) si se encuentra la persona (y devolverá la misma en formato JSON en el body de la respuesta), o un 404(Not Found) si no se encuentra.

5. Test de integración

Además de un test unitario (que no muestro en el tutorial, y que os lo podéis descargar del repositorio Git (ver Configuración y código fuente)) se ha desarrollado un test de integración.
Gracias a la integración con spring-boot, disponemos de una serie de clases y anotaciones de utilidad que nos servirán para desarrollar tests de integración de forma sencilla.
En concreto, cuando estamos desarrollando este tipo de tests con spring-boot, se realizan las siguientes acciones:

  1. Se levanta el contenedor de servlets (por defecto Tomcat 8)
  2. Se levanta el contexto de Spring
  3. Se ejecutan los tests de la clase de Test (ojo! ¡y de todos los tests de integración del proyecto! Se comparte el contenedor que se ha levantado 😉
  4. Se le manda señal de shutdown al contenedor de servlets

Nuestro test de integración al servicio REST queda como sigue:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
@WebIntegrationTest(randomPort = true)
public class PersonRestControllerIT {

    @Value("${local.server.port}")
    private int port;

    private RestTemplate restTemplate = new TestRestTemplate("user", "pass");

    @Test
    public void shouldReturnPersonDetails() {

        final ResponseEntity response = restTemplate.getForEntity(getBaseUrl() + "/person/{id}", Person.class, 1L);
        final Person person = response.getBody();

        assertThat(person.getId(), is(1L));
        assertThat(person.getName(), is("Pepe"));
        assertThat(person.getSurname(), is("Ruiz"));
        assertThat(person.getPhoneNumber(), is("666-555-444"));
        assertThat(response.getStatusCode(), is(HttpStatus.OK));
    }

    @Test
    public void shouldNotFindPersonDetailsWhenIdNotFound() {

        final ResponseEntity response = restTemplate.getForEntity(getBaseUrl() + "/person/{id}", Person.class, 3L);
        final Person person = response.getBody();

        assertThat(person, nullValue());
        assertThat(response.getStatusCode(), is(HttpStatus.NOT_FOUND));
    }

    private String getBaseUrl() {
        return new StringBuilder("http://localhost:").append(port).toString();
    }
}

Vamos a explicarlo línea a línea:

  • Línea 2. @SpringApplicationConfiguration(..): Seguramente nos recuerde a @ContextConfiguration(..), para tests de integración con Spring: enlace. Indica la configuración de la aplicación spring-boot; Le indicaremos la clase de la aplicación que contiene el main de spring-boot.
  • Línea 3. @WebIntegrationTest(randomPort = true): Esta es una de las partes más interesantes del test de integración. Esta anotación de spring-boot indica que se trata de un test de integración sobre una aplicación real.
    Implementa un bootstrapper (WebAppIntegrationTestContextBootstrapper para ser más exactos) que permite, entre otras cosas, capturar el puerto donde se ha levantado el contenedor de servlets, de forma que lo podamos caputar en el test
    (mediante un @Value(..), por ejemplo) y lo podamos usar para hacer las pruebas contra el servicio. A la hora de levantar el contenedor de servlets se le puede indicar si queremos que se levante en algún puerto específico, o en un puerto (libre) aleatorio.
  • Línea 6. Aquí se inyecta el valor de la propiedad local.server.port, que indica el puerto donde se ha levantado la aplicación spring-boot.
  • Línea 9. TestRestTemplate: Clase de spring-boot que extiende del clásico RestTemplate, y que implementa funcionalidad de autenticación básica contra servicios securizados. Usarlo es tan sencillo como añadir el usuario y la contraseña como parámetros del constructor, de forma que irán en las cabeceras de cada una de las peticiones que se realicen contra el servicio que consume.

Con esto tenemos un test de integración atacando al servicio que acabamos de implementar. Éste test estará dentro del ciclo de vida de Maven gracias al plugin maven-failsafe-plugin. Si ejecutamos mvn integration-test, en la consola se muestra lo siguiente:


09:34:47.875 [main] DEBUG o.s.t.annotation.ProfileValueUtils - Retrieved @ProfileValueSourceConfiguration [null] for test class [com.tutoriales.springbootit.controller.PersonRestControllerIT]
09:34:47.875 [main] DEBUG o.s.t.annotation.ProfileValueUtils - Retrieved ProfileValueSource type [class org.springframework.test.annotation.SystemProfileValueSource] for class [com.tutoriales.springbootit.controller.PersonRestControllerIT]
09:34:47.876 [main] DEBUG o.s.t.annotation.ProfileValueUtils - Retrieved @ProfileValueSourceConfiguration [null] for test class [com.tutoriales.springbootit.controller.PersonRestControllerIT]
09:34:47.876 [main] DEBUG o.s.t.annotation.ProfileValueUtils - Retrieved ProfileValueSource type [class org.springframework.test.annotation.SystemProfileValueSource] for class [com.tutoriales.springbootit.controller.PersonRestControllerIT]
09:34:47.878 [main] DEBUG o.s.t.annotation.ProfileValueUtils - Retrieved @ProfileValueSourceConfiguration [null] for test class [com.tutoriales.springbootit.controller.PersonRestControllerIT]
09:34:47.878 [main] DEBUG o.s.t.annotation.ProfileValueUtils - Retrieved ProfileValueSource type [class org.springframework.test.annotation.SystemProfileValueSource] for class [com.tutoriales.springbootit.controller.PersonRestControllerIT]
09:34:48.062 [main] DEBUG o.s.t.c.s.DependencyInjectionTestExecutionListener - Performing dependency injection for test context [[DefaultTestContext@15b204a1 testClass = PersonRestControllerIT, testInstance = com.tutoriales.springbootit.controller.PersonRestControllerIT@77167fb7, testMethod = [null], testException = [null], mergedContextConfiguration = [WebMergedContextConfiguration@2b9627bc testClass = PersonRestControllerIT, locations = '{}', classes = '{class com.tutoriales.springbootit.app.Application}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{server.port:0, org.springframework.boot.test.IntegrationTest=true}', resourceBasePath = '', contextLoader = 'org.springframework.boot.test.SpringApplicationContextLoader', parent = [null]]]].
09:34:48.108 [main] DEBUG o.s.core.env.StandardEnvironment - Adding [systemProperties] PropertySource with lowest search precedence
09:34:48.109 [main] DEBUG o.s.core.env.StandardEnvironment - Adding [systemEnvironment] PropertySource with lowest search precedence
09:34:48.109 [main] DEBUG o.s.core.env.StandardEnvironment - Initialized StandardEnvironment with PropertySources [systemProperties,systemEnvironment]
09:34:48.110 [main] DEBUG o.s.core.env.StandardEnvironment - Adding [integrationTest] PropertySource with search precedence immediately lower than [systemEnvironment]

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.2.5.RELEASE)

2015-07-15 09:34:48.284  INFO 13647 --- [           main] c.t.s.controller.PersonRestControllerIT  : Starting PersonRestControllerIT on mbp-jreyes with PID 13647 (/Users/jreyes/workspacesIJ/tutoriales/spring-boot-it/target/test-classes started by jreyes in /Users/jreyes/workspacesIJ/tutoriales/spring-boot-it)
2015-07-15 09:34:48.320  INFO 13647 --- [           main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@71c3b41: startup date [Wed Jul 15 09:34:48 CEST 2015]; root of context hierarchy
2015-07-15 09:34:48.653  INFO 13647 --- [           main] o.s.b.f.s.DefaultListableBeanFactory     : Overriding bean definition for bean 'beanNameViewResolver': replacing [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration; factoryMethodName=beanNameViewResolver; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/boot/autoconfigure/web/ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration.class]] with [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter; factoryMethodName=beanNameViewResolver; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter.class]]
2015-07-15 09:34:49.105  INFO 13647 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat initialized with port(s): 0 (http)
2015-07-15 09:34:49.293  INFO 13647 --- [           main] o.apache.catalina.core.StandardService   : Starting service Tomcat
2015-07-15 09:34:49.294  INFO 13647 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet Engine: Apache Tomcat/8.0.23
2015-07-15 09:34:49.375  INFO 13647 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2015-07-15 09:34:49.375  INFO 13647 --- [ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 1057 ms
2015-07-15 09:34:49.761  INFO 13647 --- [ost-startStop-1] o.s.s.web.DefaultSecurityFilterChain     : Creating filter chain: org.springframework.security.web.util.matcher.AnyRequestMatcher@1, [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@108d210b, org.springframework.security.web.context.SecurityContextPersistenceFilter@1873ae24, org.springframework.security.web.header.HeaderWriterFilter@31636d95, org.springframework.security.web.csrf.CsrfFilter@66a50ea6, org.springframework.security.web.authentication.logout.LogoutFilter@4b69f81, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@3b9a440c, org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter@306a8956, org.springframework.security.web.authentication.www.BasicAuthenticationFilter@5c0fa2db, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@fe8e14, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@4bfc72a, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@5010ac52, org.springframework.security.web.session.SessionManagementFilter@5d4c5ab2, org.springframework.security.web.access.ExceptionTranslationFilter@38eb00b9, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@34c78f7]
2015-07-15 09:34:49.804  INFO 13647 --- [ost-startStop-1] o.s.b.c.e.ServletRegistrationBean        : Mapping servlet: 'dispatcherServlet' to [/]
2015-07-15 09:34:49.809  INFO 13647 --- [ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean  : Mapping filter: 'characterEncodingFilter' to: [/*]
2015-07-15 09:34:49.809  INFO 13647 --- [ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean  : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2015-07-15 09:34:49.809  INFO 13647 --- [ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean  : Mapping filter: 'springSecurityFilterChain' to: [/*]
2015-07-15 09:34:50.014  INFO 13647 --- [           main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@71c3b41: startup date [Wed Jul 15 09:34:48 CEST 2015]; root of context hierarchy
2015-07-15 09:34:50.061  INFO 13647 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/person/{id}],methods=[GET],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public com.tutoriales.springbootit.vo.Person com.tutoriales.springbootit.controller.PersonRestController.obtainPersonDetails(java.security.Principal,long) throws com.tutoriales.springbootit.exception.PersonException
2015-07-15 09:34:50.062  INFO 13647 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],methods=[],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public org.springframework.http.ResponseEntity> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2015-07-15 09:34:50.063  INFO 13647 --- [           main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],methods=[],params=[],headers=[],consumes=[],produces=[text/html],custom=[]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest)
2015-07-15 09:34:50.084  INFO 13647 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2015-07-15 09:34:50.084  INFO 13647 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2015-07-15 09:34:50.116  INFO 13647 --- [           main] o.s.w.s.handler.SimpleUrlHandlerMapping  : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2015-07-15 09:34:50.221  INFO 13647 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 51372 (http)
2015-07-15 09:34:50.223  INFO 13647 --- [           main] c.t.s.controller.PersonRestControllerIT  : Started PersonRestControllerIT in 2.109 seconds (JVM running for 2.719)
2015-07-15 09:34:50.353  INFO 13647 --- [o-auto-1-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring FrameworkServlet 'dispatcherServlet'
2015-07-15 09:34:50.353  INFO 13647 --- [o-auto-1-exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization started
2015-07-15 09:34:50.366  INFO 13647 --- [o-auto-1-exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization completed in 13 ms
2015-07-15 09:34:50.411  INFO 13647 --- [o-auto-1-exec-1] c.t.s.controller.PersonRestController    : User: user requesting for person: 1
2015-07-15 09:34:50.472  INFO 13647 --- [o-auto-1-exec-2] c.t.s.controller.PersonRestController    : User: user requesting for person: 3
2015-07-15 09:34:50.475  WARN 13647 --- [o-auto-1-exec-2] c.t.s.controller.PersonRestController    : REST Service exception: Person has not been found!
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.792 sec
2015-07-15 09:34:50.481  INFO 13647 --- [       Thread-1] ationConfigEmbeddedWebApplicationContext : Closing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@71c3b41: startup date [Wed Jul 15 09:34:48 CEST 2015]; root of context hierarchy

6. Configuración y código fuente

El fichero de configuración de Maven tendrá esta estructura:


<project xmlns="http://maven.apache.org/POM/4.0.0"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

    <groupId>com.autentia.tutoriales</groupId>
    <artifactId>spring-boot-IT</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.2.5.RELEASE</version>
    </parent>

    <properties>
        <java.version>1.8</java.version>
        <spring.version>4.1.6.RELEASE</spring.version>
        <spring.security.version>3.2.2.RELEASE</spring.security.version>
    </properties>

    <!-- BUILD -->

    <build>
        <plugins>

            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

    <!-- DEPENDENCIES -->

    <dependencies>

        <!-- SPRING -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-web</artifactId>
            <version>${spring.security.version}</version>
        </dependency>
        <!-- Test -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-all</artifactId>
            <version>1.10.19</version>
            <scope>test</scope>
        </dependency>

        <!-- Other dependencies -->
        <dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
            <version>2.6</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-config</artifactId>
            <version>${spring.security.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-config</artifactId>
            <version>${spring.security.version}</version>
        </dependency>


    </dependencies>


    <!-- REPOSITORIES -->

    <repositories>
        <repository>
            <id>spring-releases</id>
            <url>https://repo.spring.io/libs-release</url>
        </repository>
    </repositories>
    <pluginRepositories>
        <pluginRepository>
            <id>spring-releases</id>
            <url>https://repo.spring.io/libs-release</url>
        </pluginRepository>
    </pluginRepositories>

</project>

El código del proyecto lo podéis encontrar aquí

7. Conclusiones

Con el desarrollo que se expone en este tutorial se pueden realizar soluciones de integración de componentes independientes dentro de una aplicación empresarial,
como puede ser la definición de una capa de servicios REST.
Las posibilidades que nos ofrece spring-boot para la definición de este tipo de componentes nos ahorra mucho tiempo de desarrollo,
y las clases de utilidad que nos ofrece este módulo de Spring nos permiten encapsular funcionalidad que de otra forma nos llevaría a escribir muchas líneas de código.
Además, nos permiten definir tests independientes del entorno, independientes del servidor de aplicaciones donde esté corriendo la aplicación (ya que levanta uno propio embebido), o configuración según entorno.

Una consideración que hay que tener en cuenta es que estos tests no soportan transaccionalidad (mediante @Transactional), aunque sí si le inyectamos directamente el transactionManager al test.

Por último, existe un módulo específico de testing de spring-boot: spring-boot-starter-test. Dicho módulo provee de algunas herramientas útiles para testing;
por ejemplo, te incluye las librerías mockito, hamcrest, spring-test, junit, y algunas clases de apoyo para testing. En este tutorial se ha optado por incluir las librerías de forma independiente, pero esta es otra opción igual de válida.
Échale un vistazo al apartado de referencias para saber más de este módulo de spring-boot.

8. Referencias

  • Spring security project: enlace
  • Enable web security in Spring projects: enlace
  • Spring boot test: enlace
  • Spring MVC Rest services: enlace

2 COMENTARIOS

DEJA UNA RESPUESTA

Por favor ingrese su comentario!

He leído y acepto la política de privacidad

Por favor ingrese su nombre aquí

Información básica acerca de la protección de datos

  • Responsable:
  • Finalidad:
  • Legitimación:
  • Destinatarios:
  • Derechos:
  • Más información: Puedes ampliar información acerca de la protección de datos en el siguiente enlace:política de privacidad