apuntes:testing
Differences
This shows you the differences between two versions of the page.
| Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
| apuntes:testing [2025/03/23 18:04] – [Proyectos de ejemplo] Santiago Faci | apuntes: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(' | days = getDays(new Date(' | ||
| expect(days).equal(0); | expect(days).equal(0); | ||
| + | }); | ||
| + | }); | ||
| + | </ | ||
| + | |||
| + | ==== Capa controller ==== | ||
| + | |||
| + | <code javascript> | ||
| + | const httpMocks = require(' | ||
| + | const { describe, it, expect, afterEach } = require(' | ||
| + | |||
| + | jest.mock(' | ||
| + | |||
| + | const cityController = require(' | ||
| + | |||
| + | const cityService = require(' | ||
| + | const mockedFindCities = jest.spyOn(cityService, | ||
| + | const mockedRegisterCity = jest.spyOn(cityService, | ||
| + | const { mockCityArray, | ||
| + | |||
| + | afterEach(() => { | ||
| + | jest.clearAllMocks(); | ||
| + | }); | ||
| + | |||
| + | describe(' | ||
| + | it(' | ||
| + | const response = httpMocks.createResponse(); | ||
| + | const request = httpMocks.createRequest(); | ||
| + | request.app = {}; | ||
| + | request.app.conf = {}; | ||
| + | request.path = '/ | ||
| + | |||
| + | const mockedCityList = jest.fn(async () => { | ||
| + | return mockCityArray; | ||
| + | }); | ||
| + | mockedFindCities.mockImplementation(mockedCityList); | ||
| + | |||
| + | await cityController.getCities(request, | ||
| + | expect(mockedFindCities).toHaveBeenCalledTimes(1); | ||
| + | expect(response.statusCode).toEqual(200); | ||
| + | expect(response._isEndCalled()).toBeTruthy(); | ||
| + | expect(response._getJSONData().length).toEqual(5); | ||
| + | }); | ||
| + | |||
| + | it(' | ||
| + | const response = httpMocks.createResponse(); | ||
| + | const request = httpMocks.createRequest(); | ||
| + | request.app = {}; | ||
| + | request.app.conf = {}; | ||
| + | request.path = '/ | ||
| + | request.body = mockCityToRegister; | ||
| + | |||
| + | const mockedRegisterCityResponse = jest.fn(async () => { | ||
| + | return mockCityResponse; | ||
| + | }); | ||
| + | mockedRegisterCity.mockImplementation(mockedRegisterCityResponse); | ||
| + | |||
| + | await cityController.postCity(request, | ||
| + | expect(mockedRegisterCity).toHaveBeenCalledTimes(1); | ||
| + | expect(response.statusCode).toEqual(201); | ||
| + | expect(response._isEndCalled()).toBeTruthy(); | ||
| + | expect(response._getJSONData().id).toEqual(1); | ||
| + | expect(response._getJSONData().name).toEqual(' | ||
| + | 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(' | ||
| }); | }); | ||
| }); | }); | ||
| 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: | ||
| </ | </ | ||
| - | - Caso de uso: Registro (201 Created) | + | * Caso de uso: **Registro (201 Created)** |
| <code javascript> | <code javascript> | ||
| Line 124: | Line 197: | ||
| </ | </ | ||
| - | | + | * Caso de uso: **Validación (400 Bad Request)** |
| <code javascript> | <code javascript> | ||
| Line 144: | Line 217: | ||
| }); | }); | ||
| </ | </ | ||
| + | |||
| + | <code javascript> | ||
| + | . . . | ||
| + | " | ||
| + | " | ||
| + | . . . | ||
| + | . . . | ||
| + | </ | ||
| + | |||
| + | También podemos aprovechar para modificar el script '' | ||
| + | |||
| + | <code javascript> | ||
| + | . . . | ||
| + | " | ||
| + | . . . | ||
| + | " | ||
| + | . . . | ||
| + | . . . | ||
| + | </ | ||
| + | |||
| + | Asi, podremos lanzar todos los tests solo con '' | ||
| ===== Crear un entorno de pruebas con Docker ===== | ===== Crear un entorno de pruebas con Docker ===== | ||
| Line 172: | Line 266: | ||
| <code bash> | <code bash> | ||
| - | santi@zenbook: | + | santi@zenbook: |
| </ | </ | ||
| + | |||
| + | Para destruir el entorno: | ||
| + | |||
| + | <code bash> | ||
| + | santi@zenbook: | ||
| + | </ | ||
| + | |||
| + | También se puede simplemente detener: | ||
| + | <code bash> | ||
| + | santi@zenbook: | ||
| + | </ | ||
| + | |||
| + | O bien iniciar si está detenido: | ||
| + | |||
| + | <code bash> | ||
| + | santi@zenbook: | ||
| + | </ | ||
| + | |||
| + | ====== API Testing con Postman ====== | ||
| + | |||
| + | {{postman-logo.png? | ||
| + | |||
| + | ===== 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:// | ||
| + | * https:// | ||
| + | * https:// | ||
| + | * https:// | ||
| + | |||
| + | ===== Newman ===== | ||
| + | |||
| + | Instalar [[https:// | ||
| + | |||
| + | <code bash> | ||
| + | santi@zenbook: | ||
| + | </ | ||
| + | |||
| + | Cómo lanzar una colección de Postman con Newman: | ||
| + | |||
| + | <code bash> | ||
| + | santi@zenbook: | ||
| + | </ | ||
| + | |||
| + | Cómo lanzar una colección de Postman con Newman para un entorno determinado: | ||
| + | |||
| + | <code bash> | ||
| + | santi@zenbook: | ||
| + | </ | ||
| + | |||
| + | Puedes encontrar una colección de ejemplo ya preparada con algunos scripts, lista para ser ejecutada con Newman [[https:// | ||
| + | |||
| + | ===== API testing con Postman automatizado con GitHub Actions ===== | ||
| + | |||
| + | ==== Dockerfile ==== | ||
| + | |||
| + | El primer paso será generar la imagen docker de la API que hemos desarrollado, | ||
| + | |||
| + | <code yaml> | ||
| + | FROM node: | ||
| + | WORKDIR / | ||
| + | COPY package*json ./ | ||
| + | RUN npm ci --only=production | ||
| + | COPY . . | ||
| + | EXPOSE 3000 | ||
| + | ENV NODE_ENV=production | ||
| + | CMD [" | ||
| + | </ | ||
| + | |||
| + | ==== Docker compose ==== | ||
| + | |||
| + | El siguiente es un ejemplo de un fichero '' | ||
| + | |||
| + | <code yaml> | ||
| + | version: " | ||
| + | name: cities | ||
| + | services: | ||
| + | db: | ||
| + | image: mariadb: | ||
| + | container_name: | ||
| + | environment: | ||
| + | MARIADB_USER: | ||
| + | MARIADB_PASSWORD: | ||
| + | MARIADB_PORT: | ||
| + | MARIADB_ROOT_PASSWORD: | ||
| + | volumes: | ||
| + | - ./ | ||
| + | healthcheck: | ||
| + | test: [ CMD, healthcheck.sh, | ||
| + | interval: 10s | ||
| + | timeout: 5s | ||
| + | retries: 3 | ||
| + | networks: | ||
| + | - network1 | ||
| + | backend: | ||
| + | image: cities-be | ||
| + | container_name: | ||
| + | ports: | ||
| + | - " | ||
| + | depends_on: | ||
| + | db: | ||
| + | condition: service_healthy | ||
| + | networks: | ||
| + | - network1 | ||
| + | frontend: | ||
| + | image: cities-fe | ||
| + | container_name: | ||
| + | ports: | ||
| + | - " | ||
| + | networks: | ||
| + | - network1 | ||
| + | networks: | ||
| + | network1: | ||
| + | driver: bridge</ | ||
| + | |||
| + | ==== 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: | ||
| + | | ||
| + | steps: | ||
| + | - uses: actions/ | ||
| + | - uses: actions/ | ||
| + | with: | ||
| + | node-version: | ||
| + | cache: ' | ||
| + | - 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 | ||
| + | </ | ||
| + | |||
| + | |||
| + | |||
| ====== Proyectos de ejemplo ====== | ====== Proyectos de ejemplo ====== | ||
| Line 181: | Line 430: | ||
| ---- | ---- | ||
| - | (c) 2025 Santiago Faci | + | (c) 2025-2026 Santiago Faci |
apuntes/testing.1742753041.txt.gz · Last modified: by Santiago Faci
