Table of Contents
Testing

Test unitarios
Clases de utilidad
const expect = require('chai').expect; const { getDaysFromNow, getDays} = require('../../dateUtils'); beforeAll(() => { jest.useFakeTimers(); jest.setSystemTime(new Date('2025-02-13')); }); afterAll(() => { jest.useRealTimers(); }); describe('dateUtils', () => { // TODO Añadir casos de prueba para la función getYearsFromNow it('getDaysFromNow', () => { let days = getDaysFromNow(new Date('2025-02-12')); expect(days).equal(1); days = getDaysFromNow(new Date('2025-01-13')); expect(days).equal(31); }); it('getDays', () => { let days = getDays(new Date('2025-01-12'), new Date('2025-01-15')); expect(days).equal(3); days = getDays(new Date('2025-01-01'), new Date('2024-01-01')); expect(days).equal(366); days = getDays(new Date('2024-01-01'), new Date('2024-01-01')); expect(days).equal(0); }); });
Capa controller
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'); }); });
. . . "scripts": { "unit-test": "jest src/test/unit", . . . . . .
santi@zenbook:$ npm install -g jest
Test de integración
- Caso de uso: Ok (200 OK)
const chai = require('chai'); const expect = chai.expect; const chaiHttp = require('chai-http'); const app = require('../../app').app; chai.use(chaiHttp); chai.should(); describe('cities', () => { describe('GET /cities', () => { it('should get all cities', (done) => { chai.request(app) .get('/cities') .end((error, response) => { response.should.have.status(200); response.body.should.be.a('array'); expect(response.body[0]).to.have.property('name'); expect(response.body[0]).to.have.property('altitude'); expect(response.body[0]).to.have.property('population'); expect(response.body[0]).to.have.property('foundationDate'); expect(response.body[0]).to.have.property('age'); expect(response.body[0]).to.have.property('area'); expect(response.body[0]).to.have.property('density'); expect(response.body[0].name).to.equal('Zaragoza'); expect(response.body[1].name).to.equal('Madrid'); done(); }); }); }); });
- Caso de uso: Registro (201 Created)
it('should register a new city', (done) => { chai.request(app) .post('/cities') .send({ name: 'cityName', population: 100, altitude: 200, foundationDate: '2005-10-10', area: 1000 }) .end((error, response) => { response.should.have.status(201); expect(response.body).to.have.property('id'); expect(response.body).to.have.property('name'); expect(response.body).to.have.property('population'); expect(response.body).to.have.property('altitude'); expect(response.body).to.have.property('foundationDate'); expect(response.body).to.have.property('age'); expect(response.body).to.have.property('area'); expect(response.body).to.have.property('density'); done(); }); });
- Caso de uso: Validación (400 Bad Request)
it('validation should fail because name is mandatory', (done) => { chai.request(app) .post('/cities') .send({ population: 100, altitude: 200, foundationDate: '2005-10-10', area: 1000 }) .end((error, response) => { response.should.have.status(400); expect(response.body.status).to.equal('bad-request'); expect(response.body.message).to.equal('name field is mandatory'); done(); }); });
. . . "scripts": { "integration-test": "mocha src/test/integration --exit", . . . . . .
También podemos aprovechar para modificar el script test
de forma que lance todos los tests: unitarios y de integración:
. . . "scripts": { . . . "test": "npm run unit-test && npm run integration-test", . . . . . .
Asi, podremos lanzar todos los tests solo con npm test
Crear un entorno de pruebas con Docker
Colocaremos en la carpeta db
del proyecto el script SQL que creará la base de datos y las tablas. También podemos añadir las sentencias INSERT
que queramos para preparar datos según los casos de nuestros tests de integración.
Podemos crear un fichero docker-compose.dev.yaml
con la siguiente configuración:
version: "3.4" name: cities services: db: image: mariadb:11.3.2 container_name: cities-db-dev environment: MYSQL_USER: 'user' MYSQL_PASSWORD: 'password' MYSQL_PORT: 3306, MYSQL_ROOT_PASSWORD: 'rootpassword' ports: - "3306:3306" volumes: - ./db:/docker-entrypoint-initdb.d
Y podremos lanzar nuestro entorno de pruebas con el siguiente comando:
santi@zenbook:$ docker compose -f docker.compose.dev.yaml up -d
Para destruir el entorno:
santi@zenbook:$ docker compose -f docker.compose.dev.yaml down
También se puede simplemente detener:
santi@zenbook:$ docker compose -f docker.compose.dev.yaml stop
O bien iniciar si está detenido:
santi@zenbook:$ docker compose -f docker.compose.dev.yaml start
Proyectos de ejemplo
En cities puedes encontrar un proyecto con tests unitarios y de integración implementados. También está lo necesario para crear un entorno de pruebas y lanzar todos los tests utilizando dicho entorno.
© 2025 Santiago Faci