Entornos de desarrollo

1º DAM/DAW - Curso 2024-2025

User Tools

Site Tools


apuntes:testing

This is an old revision of the document!


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 service

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();
    });
});

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

apuntes/testing.1742776502.txt.gz · Last modified: 2025/03/24 00:35 by Santiago Faci