Entornos de desarrollo

1º DAM/DAW - Curso 2026-2027

User Tools

Site Tools


apuntes:testing

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
apuntes:testing [2025/03/23 18:05] – [Crear un entorno de pruebas con Docker] Santiago Faciapuntes:testing [2026/09/28 22:10] (current) – [Test de integración] Santiago Faci
Line 7: Line 7:
  
 ===== Test unitarios ===== ===== Test unitarios =====
 +
 +Se implementan para testear la unidad mínima de código, que normalmente serán funciones o métodos, de forma totalmente aislada del resto de la aplicación.
 +==== Clases de utilidad ====
  
 <code javascript> <code javascript>
Line 41: Line 44:
         days = getDays(new Date('2024-01-01'), new Date('2024-01-01'));         days = getDays(new Date('2024-01-01'), new Date('2024-01-01'));
         expect(days).equal(0);         expect(days).equal(0);
 +    });
 +});
 +</code>
 +
 +==== Capa controller ====
 +
 +<code javascript>
 +const httpMocks = require('node-mocks-http');
 +const { describe, it, expect, afterEach } = require('@jest/globals');
 +
 +jest.mock('../../service/cities');
 +
 +const cityController = require('../../controller/cities');
 +
 +const cityService = require('../../service/cities');
 +const mockedFindCities = jest.spyOn(cityService, 'findCities');
 +const mockedRegisterCity = jest.spyOn(cityService, "registerCity");
 +const { mockCityArray, mockCityToPost, mockCityResponse, mockCityToRegister } = require('./mocks/cities');
 +
 +afterEach(() => {
 +    jest.clearAllMocks();
 +});
 +
 +describe('cities', () => {
 +    it('GET /cities should get a city list', async () => {
 +        const response = httpMocks.createResponse();
 +        const request = httpMocks.createRequest();
 +        request.app = {};
 +        request.app.conf = {};
 +        request.path = '/cities';
 +
 +        const mockedCityList = jest.fn(async () => {
 +            return mockCityArray;
 +        });
 +        mockedFindCities.mockImplementation(mockedCityList);
 +
 +        await cityController.getCities(request, response);
 +        expect(mockedFindCities).toHaveBeenCalledTimes(1);
 +        expect(response.statusCode).toEqual(200);
 +        expect(response._isEndCalled()).toBeTruthy();
 +        expect(response._getJSONData().length).toEqual(5);
 +    });
 +
 +    it('POST /cities should register a new city', async () => {
 +        const response = httpMocks.createResponse();
 +        const request = httpMocks.createRequest();
 +        request.app = {};
 +        request.app.conf = {};
 +        request.path = '/cities';
 +        request.body = mockCityToRegister;
 +
 +        const mockedRegisterCityResponse = jest.fn(async () => {
 +            return mockCityResponse;
 +        });
 +        mockedRegisterCity.mockImplementation(mockedRegisterCityResponse);
 +
 +        await cityController.postCity(request, response);
 +        expect(mockedRegisterCity).toHaveBeenCalledTimes(1);
 +        expect(response.statusCode).toEqual(201);
 +        expect(response._isEndCalled()).toBeTruthy();
 +        expect(response._getJSONData().id).toEqual(1);
 +        expect(response._getJSONData().name).toEqual('Zaragoza');
 +        expect(response._getJSONData().population).toEqual(700000);
 +        expect(response._getJSONData().altitude).toEqual(200);
 +        expect(response._getJSONData().age).toEqual(5);
 +        expect(response._getJSONData().area).toEqual(734734);
 +        expect(response._getJSONData().density).toEqual(2373);
 +        expect(response._getJSONData().foundationDate).toEqual('2020-12-01');
     });     });
 }); });
Line 59: Line 130:
 ===== Test de integración ===== ===== Test de integración =====
  
- - Caso de uso: Ok (200 OK)+En este caso escribimos tests que permitan probar varios componentes juntos para verificar que trabajan bien cuando interactuan entre si. 
 + 
 +  * Caso de uso: **Ok (200 OK)**
  
 <code javascript> <code javascript>
Line 96: Line 169:
 </code> </code>
  
-- Caso de uso: Registro (201 Created)+  * Caso de uso: **Registro (201 Created)**
  
 <code javascript> <code javascript>
Line 124: Line 197:
 </code> </code>
  
- - Caso de uso: Validación (400 Bad Request)+  * Caso de uso: **Validación (400 Bad Request)**
  
 <code javascript> <code javascript>
Line 144: Line 217:
 }); });
 </code> </code>
 +
 +<code javascript>
 +. . .
 +"scripts": {
 +    "integration-test": "mocha src/test/integration --exit",
 +    . . .
 +. . .
 +</code>
 +
 +También podemos aprovechar para modificar el script ''test'' de forma que lance todos los tests: unitarios y de integración:
 +
 +<code javascript>
 +. . .
 +"scripts": {
 +    . . .
 +    "test": "npm run unit-test && npm run integration-test",
 +    . . . 
 +. . .
 +</code>
 +
 +Asi, podremos lanzar todos los tests solo con ''npm test''
  
 ===== Crear un entorno de pruebas con Docker ===== ===== Crear un entorno de pruebas con Docker =====
Line 191: Line 285:
 santi@zenbook:$ docker compose -f docker.compose.dev.yaml start santi@zenbook:$ docker compose -f docker.compose.dev.yaml start
 </code> </code>
 +
 +====== API Testing con Postman ======
 +
 +{{postman-logo.png?100 }}
 +
 +===== Test scripts =====
 +
 +En la propia documentación de Postman hay un montón de ejemplos de todo lo que es posible hacer con los test scripts de esta herramienta:
 +
 +  * https://www.postman.com/api-platform/api-testing/
 +  * https://learning.postman.com/docs/writing-scripts/script-references/test-examples/
 +  * https://learning.postman.com/docs/writing-scripts/script-references/variables-list/
 +  * https://learning.postman.com/docs/writing-scripts/script-references/postman-sandbox-api-reference/
 +
 +===== Newman ===== 
 +
 +Instalar [[https://learning.postman.com/docs/collections/using-newman-cli/command-line-integration-with-newman|Newman]] de forma global como comando en tu equipo, listo para ejecutarse directamente desde la consola (Si aún no tienes Node.js instalado, sigue [[https://docs.npmjs.com/downloading-and-installing-node-js-and-npm|estas instrucciones]]):
 +
 +<code bash>
 +santi@zenbook:$ npm install -g newman
 +</code>
 +
 +Cómo lanzar una colección de Postman con Newman:
 +
 +<code bash>
 +santi@zenbook:$ newman run api-collection.postman_collection.json
 +</code>
 +
 +Cómo lanzar una colección de Postman con Newman para un entorno determinado:
 +
 +<code bash>
 +santi@zenbook:$ newman run -e localhost.postman_environment.json api-collection.postman_collection.json
 +</code>
 +
 +Puedes encontrar una colección de ejemplo ya preparada con algunos scripts, lista para ser ejecutada con Newman [[https://github.com/codeandcoke/cities-api/blob/main/cities.postman_collection.json|aqui]]
 +
 +===== API testing con Postman automatizado con GitHub Actions =====
 +
 +==== Dockerfile ====
 +
 +El primer paso será generar la imagen docker de la API que hemos desarrollado, para que podamos lanzarla luego como un contenedor docker
 +
 +<code yaml>
 +FROM node:22-alpine
 +WORKDIR /usr/src/app
 +COPY package*json ./
 +RUN npm ci --only=production
 +COPY . .
 +EXPOSE 3000
 +ENV NODE_ENV=production
 +CMD ["node", "src/app.js"]
 +</code>
 +
 +==== Docker compose ====
 +
 +El siguiente es un ejemplo de un fichero ''docker-compose.yml'' listo para lanzar la API + Base de datos como contenedores docker
 +
 +<code yaml>
 +version: "3.4"
 +name: cities
 +services:
 +  db:
 +    image: mariadb:11.3.2
 +    container_name: cities-db
 +    environment:
 +      MARIADB_USER: 'user'
 +      MARIADB_PASSWORD: 'password'
 +      MARIADB_PORT: 3306,
 +      MARIADB_ROOT_PASSWORD: 'rootpassword'
 +    volumes:
 +      - ./db:/docker-entrypoint-initdb.d
 +    healthcheck:
 +      test: [ CMD, healthcheck.sh, --connect, --innodb_initialized]
 +      interval: 10s
 +      timeout: 5s
 +      retries: 3
 +    networks:
 +      - network1
 +  backend:
 +    image: cities-be
 +    container_name: cities-api
 +    ports:
 +      - "8080:8080"
 +    depends_on:
 +      db:
 +        condition: service_healthy
 +    networks:
 +      - network1
 +  frontend:
 +    image: cities-fe
 +    container_name: cities-ui
 +    ports:
 +      - "1234:1234"
 +    networks:
 +      - network1
 +networks:
 +  network1:
 +    driver: bridge</code>
 +
 +==== GitHub action ====
 +
 +Por último, configuramos un workflow de GitHub Actions que hará lo siguiente:
 +  * Generamos la imagen docker de nuestra API
 +  * Lanzará nuestra API + Base de datos como contenedores docker
 +  * Instalará Newman
 +  * Lanzará la colección con los tests con Newman sobre la API que se acaba de lanzar
 +
 +<code yaml>
 +name: Integration tests with Newman
 +
 +on:
 +  pull_request:
 +    branches:
 +      - main
 +
 +jobs:
 +  newman:
 +    runs-on: ubuntu-latest
 +    
 +    strategy:
 +      matrix:
 +        node-version: [20.x, 22.x, 24.x]
 +    
 +    steps:
 +      - uses: actions/checkout@master
 +      - uses: actions/setup-node@v4
 +        with:
 +          node-version: ${{ matrix.node-version }}
 +          cache: 'npm'
 +      - run: npm install
 +      - run: docker build -t cities-api .
 +      - run: docker compose up -d
 +      - run: npm install -g newman
 +      - run: newman run cities.postman_collection.json -e local.postman_environment.json
 +</code>
 +
 +
 +
  
 ====== Proyectos de ejemplo ====== ====== Proyectos de ejemplo ======
Line 198: Line 430:
 ---- ----
  
-(c) 2025 Santiago Faci+(c) 2025-2026 Santiago Faci
apuntes/testing.1742753133.txt.gz · Last modified: by Santiago Faci