Desarrollo basado en pruebas para las API de Spring Boot

     

    Introducción

    Con el aumento de la adopción de teléfonos inteligentes en el mundo actual, ha habido una afluencia de aplicaciones móviles para lograr una amplia variedad de tareas. Algunas de las aplicaciones que usamos a diario se comunican con otros sistemas para brindarnos una experiencia perfecta en múltiples dispositivos y plataformas.

    ¿Cómo es esto posible? Las interfaces de programación de aplicaciones (API) son responsables de esta conectividad ampliada. Hacen posible que las aplicaciones móviles y web interactúen y faciliten la transferencia de datos entre ellas y otros sistemas.

    En este artículo, analizaremos las API, las mejores prácticas al crearlas y también crearemos una API utilizando el enfoque de desarrollo basado en pruebas y el marco Spring Boot .

    El auge de las API

    Una API define un conjunto de rutinas y protocolos para la interacción entre sistemas de software. Muchas aplicaciones móviles y web interactúan con servidores que manejan solicitudes y responden a ellas, lo que se conoce como clientes.

    A medida que los sistemas aumentan de tamaño, se vuelven robustos y pueden resultar difíciles de mantener y realizar actualizaciones. Al desacoplar un sistema en varias API específicas, se logra flexibilidad y las partes del sistema robusto ahora pueden actualizarse o implementarse en partes fácilmente sin afectar el tiempo de actividad o el rendimiento del resto del sistema.

    Esto da como resultado una arquitectura de microservicios, que depende en gran medida del desarrollo de API. En dicho sistema, las API proporcionan un modo de comunicación dentro del sistema y las diversas partes del sistema aún pueden interactuar y compartir la carga de trabajo.

    Los teléfonos inteligentes nos permitieron estar conectados y con su poder cada vez mayor, podemos lograr mucho más. El acceso a Internet también se ha vuelto más común, por lo que la mayoría de los teléfonos inteligentes están constantemente conectados a Internet. Estos dos factores impulsan el uso de aplicaciones móviles que interactúan con servidores web donde las API entran en escena.

    Las API facilitan la comunicación entre las aplicaciones móviles y los servidores y el aumento en el uso de aplicaciones móviles ha impulsado el aumento de las API.

    Las aplicaciones web también han evolucionado con el tiempo y la complejidad ha aumentado. Esto, a su vez, ha llevado a la separación de las capas de presentación y lógica de una aplicación web normal. Inicialmente, tendría ambas capas de una aplicación web compiladas juntas y desplegadas como una para que la usen las masas. Ahora, la sección del frontend está desacoplada del backend para facilitar la separación de preocupaciones.

    Las API también permiten a las empresas realizar una configuración de backend única para servir aplicaciones móviles y aplicaciones web al mismo tiempo. Esto ahorra tiempo de desarrollo y deuda técnica, ya que el sistema backend solo se modifica en un punto.

    Los teléfonos inteligentes también son tan diversos y las empresas ahora tienen que atender a varios tipos de teléfonos inteligentes al mismo tiempo para brindar una experiencia uniforme a sus usuarios. Las API hacen posible que las aplicaciones móviles que se ejecutan en diferentes plataformas interactúen de manera uniforme con un único sistema backend o API.

    Es muy importante mencionar que las API también hacen posible que otros desarrolladores que utilizan diferentes lenguajes de programación accedan a nuestro sistema para obtener información. Esto facilita la integración de sistemas que utilizan diferentes lenguajes de programación.

    Esto, de nuevo, nos permite crear aplicaciones modulares, utilizando varios lenguajes, herramientas y marcos juntos para sacar lo mejor de cada uno.

    Construyendo mejores API

    Las API también actúan como un punto de contacto con el trabajo de otros desarrolladores, ya que pueden permitir que otros desarrolladores las consuman para su propio uso.

    Por ejemplo, Twitter ha expuesto algunas de sus API para que las utilicen otros desarrolladores para crear otros clientes de Twitter y utilizar la plataforma de otras formas únicas. Algunos han construido bots en plataformas como Telegram para enviar tweets o buscar tweets, todo lo cual se logra a través de API.

    Esto hace que las API sean importantes en los ecosistemas de software actuales y futuros, ya que nos permiten integrarnos con otros sistemas de manera flexible. No solo API, sino buenas API.

    Es primordial que nuestra API esté bien construida y documentada para que cualquier otra persona que la consuma tenga más facilidad. La documentación es el aspecto más importante de una API, le permite a otros desarrolladores saber qué logra y qué se requiere para aprovechar esa funcionalidad. También ayuda a los encargados de mantenimiento a saber con qué se enfrentan y asegurarse de que sus cambios no afecten ni interrumpan la funcionalidad existente.

    Los códigos de estado HTTP se definieron para identificar diversas situaciones que pueden ocurrir cuando una aplicación interactúa con una API.

    Se dividen en cinco categorías que incluyen códigos para:

    • Respuestas informativas : estados 1xx , como 100 Continuar, 101 Protocolos de conmutación, etc.
    • Éxito : estados 2xx , como 200 OK, 202 Aceptado, etc.
    • Redirección : estados 3xx , como 300 opciones múltiples, 301 movido permanentemente, etc.
    • Errores del cliente : estados 4xx , como 400 Bad Request, 403 Forbidden, 404 Not Found, etc.
    • Errores del servidor : estados 5xx , como 500 Internal Server Error, 502 Bad Gateway, 503 Service no disponible, etc.

    Estos códigos ayudan al sistema y a las personas que interactúan con él a identificar y comprender la naturaleza de los eventos que ocurren y las causas de los errores.

    Al adherirnos a los códigos de estado HTTP en nuestras API, podemos hacer que nuestras API sean fáciles de interactuar e integrar. Además de estos, también podemos definir nuestros propios códigos de error para nuestras API, pero es importante que los documentemos claramente para que sea más fácil para los consumidores y mantenedores de las API.

    Antes de que los automóviles, teléfonos o dispositivos electrónicos se entreguen a sus usuarios, se someten a pruebas exhaustivas para garantizar que no funcionen mal cuando están en uso. Las API se han vuelto más comunes e importantes, por lo tanto, también necesitan la misma cantidad de atención a los detalles.

    Deben probarse minuciosamente antes de su lanzamiento para evitar un mal funcionamiento durante la producción.

    Construyendo una API

    Arquitectura del proyecto

    Supongamos que estamos creando una aplicación que ayuda a los usuarios a mantener una lista de sus coches. Podrán agregar autos nuevos, actualizar autos existentes e incluso eliminar autos que ya no poseen. Esta aplicación estará disponible para dispositivos Android e iOS y también como aplicación web.

    Usando Spring Boot Framework, podemos construir una única API que pueda servir a las tres aplicaciones, o clientes, simultáneamente.

    Nuestro viaje comienza en la herramienta Spring Initializer que nos ayuda a iniciar rápidamente nuestra API Spring Boot en cuestión de minutos. Hay muchas dependencias y paquetes que nos ayudan a lograr varias funcionalidades en nuestras API y la herramienta Spring Initializer ayuda a integrarlas en nuestro proyecto de inicio.

    Esto tiene como objetivo facilitar nuestro proceso de desarrollo y permitirnos dirigir nuestra atención a la lógica de nuestra aplicación:

    La herramienta nos permite elegir entre Maven y Gradle , que son herramientas que nos ayudan a automatizar algunos aspectos de nuestro flujo de trabajo de compilación, como probar, ejecutar y empaquetar nuestra aplicación Java. También tenemos la opción de elegir entre usar Java o Kotlin cuando construimos nuestra API usando Spring Boot para lo cual podemos especificar la versión.

    Cuando hacemos clic en “Cambiar a la versión completa”, obtenemos más opciones para agrupar en nuestra API. Muchas de estas opciones son útiles al crear microservicios, como las secciones “Cloud Config” y “Cloud Discovery”.

    Para nuestra API, elegiremos las siguientes dependencias:

    • Web para ayudarnos a desarrollar una API basada en web
    • MySQL que nos ayudará a conectarnos a nuestra base de datos MySQL,
    • JPA que es la API de persistencia de Java para satisfacer nuestras necesidades de interacción con la base de datos, y
    • Actuator para ayudarnos a mantener y monitorear nuestra aplicación web.

    Con las dependencias establecidas, hacemos clic en el botón “Generar proyecto” para obtener un zip que contiene nuestro código estándar.

    Identifiquemos lo que viene en el paquete usando el treecomando:

    $ tree .
    .
    ├── HELP.md
    ├── mvnw
    ├── mvnw.cmd
    ├── pbcopy
    ├── pom.xml
    └── src
        ├── main
        │   ├── java
        │   │   └── com
        │   │       └── example
        │   │           └── cars
        │   │               └── CarsApplication.java
        │   └── resources
        │       ├── application.properties
        │       ├── static
        │       └── templates
        └── test
            └── java
                └── com
                    └── example
                        └── cars
                            └── CarsApplicationTests.java
    

    En la carpeta raíz, hay un pom.xmlarchivo que contiene la configuración del proyecto para nuestra API Spring Boot. Si usáramos Gradle, tendríamos un build.gradlearchivo en su lugar. Incluye información como los detalles de nuestra nueva API y todas sus dependencias.

    Trabajaremos principalmente en las carpetas mainy testdentro de la srccarpeta source ( ). Aquí es donde colocaremos nuestros controladores, modelos, clases de utilidad entre otros.

    Comencemos creando nuestra base de datos y configurando nuestra API para usarla. Siga esta guía para instalar y verificar que MySQL se esté ejecutando.

    Una vez listo, creemos nuestra base de datos de la siguiente manera:

    $ mysql -u root -p
    
    mysql> CREATE DATABASE cars_database;
    Query OK, 1 row affected (0.08 sec)
    

    Algunos detalles de nuestro servicio serán diferentes de un entorno a otro. Por ejemplo, la base de datos que usamos durante el desarrollo no será la misma que los usuarios finales usarán para almacenar su información.

    Los archivos de configuración nos facilitan el cambio de esos detalles, lo que facilita la migración y modificación de nuestra API. Esto se logra a través del archivo de configuración, que en una API de Spring Boot es el application.propertiesarchivo que se encuentra en la src/main/resourcescarpeta.

    Para permitir que nuestra dependencia JPA acceda y modifique nuestra base de datos, modificamos el archivo de configuración agregando las propiedades:

    # Database Properties
    spring.datasource.url = jdbc:mysql://localhost:3306/cars_database?useSSL=false
    spring.datasource.username = root
    spring.datasource.password = password
    
    # Hibernate Properties
    # The SQL dialect makes Hibernate generate better SQL for the chosen database
    spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect
    
    # Hibernate ddl auto (create, create-drop, validate, update)
    spring.jpa.hibernate.ddl-auto = update
    

    Ahora necesitamos una clase de entidad para definir los recursos de nuestra API y sus detalles, ya que se guardarán en nuestra base de datos. A Cares nuestro recurso en esta API y lo que esto significa es que representa nuestro objeto o elemento de la vida real en cuya información realizaremos acciones. Tales acciones incluyen Crear, Leer, Actualizar y Eliminar, simplemente como operaciones CRUD .

    Estas operaciones están detrás de los métodos o verbos HTTP que se refieren a varias operaciones que una API puede exponer. Incluyen:

    • GET que es una operación de lectura que solo obtiene los datos especificados,
    • POSTque posibilita la creación de resourcesmediante el suministro de su información como parte de la solicitud,
    • PUT que nos permite modificar un recurso, y
    • DELETE que usamos para eliminar un recurso y su información de nuestra API.

    Para organizar mejor nuestro código, introduciremos algunas carpetas más en nuestro proyecto a src/main/java/com/example/cars/nivel. Agregaremos una carpeta llamada modelspara alojar las clases que definen nuestros objetos.

    Las otras carpetas que se agregarán incluyen una controllerscarpeta que contiene nuestros controladores, una repositorycarpeta para las clases de administración de la base de datos y una utilscarpeta para cualquier clase auxiliar que necesitemos agregar a nuestro proyecto. La estructura de carpetas resultante será:

    $ tree .
    .
    ├── HELP.md
    ├── mvnw
    ├── mvnw.cmd
    ├── pbcopy
    ├── pom.xml
    └── src
        ├── main
        │   ├── java
        │   │   └── com
        │   │       └── example
        │   │           └── cars
        │   │               ├── CarsApplication.java
        │   │               ├── controllers
        │   │               ├── models
        │   │               ├── repository
        │   │               └── utils
        │   └── resources
        │       ├── application.properties
        │       ├── static
        │       └── templates
        └── test
            └── java
                └── com
                    └── example
                        └── cars
                            └── CarsApplicationTests.java
    
    

    Modelo de dominio

    Definamos nuestra Carclase en la modelscarpeta:

    /**
    * This class will represent our car and its attributes
    */
    @Entity
    @Table(name="cars") // the table in the database tht will contain our cars data
    @EntityListeners(AuditingEntityListener.class)
    public class Car {
    
        @Id
        @GeneratedValue(strategy=GenerationType.AUTO)
        private long id; // Each car will be given an auto-generated unique identifier when stored
    
        @Column(name="car_name", nullable=false)
        private String carName; // We will also save the name of the car
    
        @Column(name="doors", nullable=false)
        private int doors; // We will also save the number of doors that a car has
    
        // getters and setters
    }
    

    Nota : he eliminado las importaciones para acortar el fragmento de código. Consulte el repositorio de Github adjunto al final del artículo para obtener el código completo.

    DAO

    Con nuestro modelo de automóvil listo, creemos ahora el CarRepositoryarchivo que se utilizará en la interacción con la base de datos:

    public interface CarRepository extends JpaRepository<Car, Long> { }
    

    Pruebas de escritura

    Ahora podemos exponer la funcionalidad de nuestra API a través de nuestro controller, pero en el espíritu del desarrollo basado en pruebas (TDD), escribamos las pruebas primero en el CarsApplicationTestsarchivo:

    // These are a subset of the tests, the full test file is available on the Github repo attached at the end of this article
    ....
    
        /**
         * Here we test that we can get all the cars in the database
         * using the GET method
         */
        @Test
        public void testGetAllCars() {
            HttpHeaders headers = new HttpHeaders();
            HttpEntity<String> entity = new HttpEntity<String>(null, headers);
    
            ResponseEntity<String> response = restTemplate.exchange(getRootUrl() + "/cars",
                HttpMethod.GET, entity, String.class);
    
            Assert.assertNotNull(response.getBody());
        }
    
        /**
         * Here we test that we can fetch a single car using its id
         */
        @Test
        public void testGetCarById() {
            Car car = restTemplate.getForObject(getRootUrl() + "/cars/1", Car.class);
            System.out.println(car.getCarName());
            Assert.assertNotNull(car);
        }
    
        /**
         * Here we test that we can create a car using the POST method
         */
        @Test
        public void testCreateCar() {
            Car car = new Car();
            car.setCarName("Prius");
            car.setDoors(4);
    
            ResponseEntity<Car> postResponse = restTemplate.postForEntity(getRootUrl() + "/cars", car, Car.class);
            Assert.assertNotNull(postResponse);
            Assert.assertNotNull(postResponse.getBody());
        }
    
        /**
         * Here we test that we can update a car's information using the PUT method
         */
        @Test
        public void testUpdateCar() {
            int id = 1;
            Car car = restTemplate.getForObject(getRootUrl() + "/cars/" + id, Car.class);
            car.setCarName("Tesla");
            car.setDoors(2);
    
            restTemplate.put(getRootUrl() + "/cars/" + id, car);
    
            Car updatedCar = restTemplate.getForObject(getRootUrl() + "/cars/" + id, Car.class);
            Assert.assertNotNull(updatedCar);
        }
    

    Las pruebas simulan diversas acciones que son posibles en nuestra API y esta es nuestra forma de verificar que la API funciona como se espera. Si se hiciera un cambio mañana, las pruebas ayudarán a determinar si alguna de las funciones de la API está rota y, al hacerlo, evitarán que rompamos la funcionalidad al realizar cambios.

    Piense en las pruebas como una lista de compras cuando vaya al supermercado. Sin él, podríamos terminar eligiendo casi todo lo que encontramos y creemos que podría ser útil. Puede que nos lleve mucho tiempo conseguir todo lo que necesitamos. Si tuviéramos una lista de compras, podríamos comprar exactamente lo que necesitamos y terminar de comprar más rápido. Las pruebas hacen lo mismo para nuestras API, nos ayudan a definir el alcance de la API para que no implementemos funcionalidad que no estaba en los planes o no era necesaria.

    Cuando ejecutamos nuestras pruebas usando el mvn testcomando, veremos errores surgidos y esto se debe a que aún no hemos implementado la funcionalidad que satisface nuestros casos de prueba.

    En TDD, primero escribimos las pruebas, las ejecutamos para asegurarnos de que inicialmente fallan y luego implementamos la funcionalidad para que las pruebas pasen.

    TDD es un proceso iterativo de escribir pruebas e implementar la funcionalidad para hacer que las pruebas pasen. Si introducimos cambios en el futuro, primero escribiremos las pruebas y luego implementaremos los cambios para que pasen las nuevas pruebas.

    Controlador

    Implementemos ahora nuestra funcionalidad API en una CarControllerque va a la controllerscarpeta:

    @RestController
    @RequestMapping("/api/v1")
    public class CarController {
    
        @Autowired
        private CarRepository carRepository;
    
        // GET Method for reading operation
        @GetMapping("/cars")
        public List<Car> getAllCars() {
            return carRepository.findAll();
        }
    
        // GET Method for Read operation
        @GetMapping("/cars/{id}")
        public ResponseEntity<Car> getCarsById(@PathVariable(value = "id") Long carId)
            throws ResourceNotFoundException {
    
            Car car = carRepository
                      .findById(carId)
                      .orElseThrow(() -> new ResourceNotFoundException("Car not found on :: " + carId));
            return ResponseEntity.ok().body(car);
        }
    
        // POST Method for Create operation
        @PostMapping("/cars")
        public Car createCar(@Valid @RequestBody Car car) {
            return carRepository.save(car);
        }
    
        // PUT Method for Update operation
        @PutMapping("/cars/{id}")
        public ResponseEntity<Car> updateCar(
            @PathVariable(value = "id") Long carId, @Valid @RequestBody Car carDetails)
            throws ResourceNotFoundException {
                Car car = carRepository
                          .findById(carId)
                          .orElseThrow(() -> new ResourceNotFoundException("Car " + carId + " not found"));
    
            car.setCarName(carDetails.getCarName());
            car.setDoors(carDetails.getDoors());
    
            final Car updatedCar = carRepository.save(car);
            return ResponseEntity.ok(updatedCar);
        }
    
        // DELETE Method for Delete operation
        @DeleteMapping("/car/{id}")
        public Map<String, Boolean> deleteCar(@PathVariable(value = "id") Long carId) throws Exception {
            Car car = carRepository
                      .findById(carId)
                      .orElseThrow(() -> new ResourceNotFoundException("Car " + carId + " not found"));
    
            carRepository.delete(car);
            Map<String, Boolean> response = new HashMap<>();
            response.put("deleted", Boolean.TRUE);
            return response;
        }
    }
    

    En la parte superior, tenemos la @RestControlleranotación para definir nuestra CarControllerclase como el controlador de nuestra API Spring Boot. Lo que sigue es @RequestMappingdonde especificamos la ruta base de nuestra URL de API como /api/v1. Esto también incluye la versión.

    El control de versiones es una buena práctica en una API para mejorar la compatibilidad con versiones anteriores. Si la funcionalidad cambia y ya tenemos a otras personas consumiendo nuestras API, podemos crear una nueva versión y hacer que ambas se ejecuten al mismo tiempo para darles tiempo suficiente para migrar a la nueva API.

    Anteriormente, aprendimos sobre las operaciones Crear, Leer, Actualizar y Eliminar en una API y cómo se asignan a los métodos HTTP. Estos métodos se alojan en el marco del resorte como PostMapping, GetMapping, PutMappingy DeleteMappinganotaciones, respectivamente. Cada una de estas anotaciones nos ayuda a exponer los puntos finales que solo realizan la operación CRUD especificada.

    También podemos tener un único punto final que maneje varios métodos HTTP:

    @RequestMapping(value="/cars", method = { RequestMethod.GET, RequestMethod.POST })
    

    Ahora que hemos implementado la funcionalidad, ejecutemos nuestras pruebas:

    Las pruebas aprobadas nos muestran que hemos implementado la funcionalidad deseada al escribir las pruebas y nuestra API funciona.

    Permítanos interactuar con nuestra API a través de Postman , que es una herramienta que ayuda a interactuar con las API al desarrollarlas o consumirlas.

    Empezamos por buscar todos los coches que tenemos almacenados en nuestra base de datos:

    Al principio, no tenemos coches almacenados. Agreguemos nuestro primer auto:

    La respuesta es el idy los detalles del coche que acabamos de añadir. Si agregamos algunos autos más y recuperamos todos los autos que hemos guardado:

    Estos son los autos que hemos creado usando nuestra API Spring Boot. Una comprobación rápida de la base de datos devuelve la misma lista:

    IU de Swagger

    Hemos construido y probado nuestra API usando TDD y ahora para mejorar nuestra API, la documentaremos usando Swagger UI , lo que nos permite crear una interfaz generada automáticamente para que otros usuarios interactúen y aprendan sobre nuestra API.

    Primero, agreguemos las siguientes dependencias en nuestro pom.xml:

    <dependency>
      <groupId>io.springfox</groupId>
      <artifactId>springfox-swagger2</artifactId>
      <version>2.7.0</version>
    </dependency>
    
    <dependency>
      <groupId>io.springfox</groupId>
      <artifactId>springfox-swagger-ui</artifactId>
      <version>2.7.0</version>
    </dependency>
    

    A continuación, crearemos un SwaggerConfig.javaen la misma carpeta que CarsApplication.java, que es el punto de entrada a nuestra API.

    El SwaggerConfig.javaarchivo también permite agregar información sobre nuestra API:

    @Configuration
    @EnableSwagger2
    public class SwaggerConfig {
        @Bean
        public Docket api() {
            return new Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.example.cars"))
                .paths(PathSelectors.any())
                .build()
                .apiInfo(metadata());
        }
    
        /**
         * Adds metadata to Swagger
         *
         * @return
         */
        private ApiInfo metadata() {
            return new ApiInfoBuilder()
                .title("Cars API")
                .description("An API to store car details built using Spring Boot")
                .build();
        }
    }
    

    Ahora anotamos nuestros puntos finales para que aparezcan en la interfaz de IU de Swagger que se generará. Esto se consigue de la siguiente manera:

    // Add this import in our controller file...
    import io.swagger.annotations.ApiOperation;
    
    // ...then annotate our HTTP Methods
    @ApiOperation(value="Fetches all cars in the database", response=Car.class)
    @PostMapping("/...") // Our endpoint
    

    Hemos especificado nuestra clase de respuesta como la Carclase, ya que es la que se utilizará para completar los detalles de nuestras respuestas. Hemos hecho esto porque Swagger UI nos permite agregar información sobre las cargas útiles de la solicitud y los detalles de la respuesta. Esto ayudará a proporcionar más información sobre las cargas útiles, como el tipo de valores que requiere nuestra API y el tipo de respuesta que se devolverá. También podemos especificar campos obligatorios en la documentación.

    En nuestro caso, también usaremos la Carclase para formatear y validar nuestros parámetros de solicitud. Por lo tanto, anotamos sus “captadores” de la siguiente manera:

        @ApiModelProperty(name="id",
                          value="The id of the car",
                          example="1")
        public long getId() {
            return id;
        }
    
        @ApiModelProperty(name="carName",
                          value="The name of the car to be saved",
                          example="Bugatti",
                          required=true)
        public String getCarName() {
            return carName;
        }
    
        @ApiModelProperty(name="doors",
                          value="The number of doors that the car has",
                          example="2",
                          required=true)
        public int getDoors() {
            return doors;
        }
    

    ¡Eso es todo! Nuestra documentación está lista. Cuando ejecutamos nuestra API usando mvn spring-boot:runy navegamos http://localhost:8080/swagger-ui.html, podemos ver la documentación de nuestra API:

    La interfaz de usuario de Swagger ha documentado todos nuestros puntos finales e incluso ha proporcionado funciones para interactuar con nuestra API directamente desde la documentación. Como se puede ver en la sección inferior derecha de la captura de pantalla, nuestros valores de ejemplo se han completado previamente para que podamos probar rápidamente la API sin tener que volver a escribir los valores.

    Conclusión

    Java es un lenguaje poderoso y hemos aprovechado su poder para construir una interfaz de programación de aplicaciones, o API, utilizando el marco Spring Boot. Hemos podido implementar cuatro de los métodos HTTP para manejar las diversas operaciones de creación, lectura, actualización y eliminación de los detalles de nuestros coches.

    Swagger UI también nos ha permitido documentar nuestra API de una manera simple pero detallada y tener esta documentación expuesta como un punto final en nuestro servicio. Habiendo notado las ventajas del desarrollo basado en pruebas, seguimos adelante y escribimos pruebas para nuestros puntos finales y nos aseguramos de que nuestra funcionalidad y pruebas estén alineadas.

    El código fuente de este proyecto está disponible aquí en Github .

     

    Etiquetas:

    Deja una respuesta

    Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *