Las pruebas unitarias lo que hacen es tomar todos tus proyectos o esos bloques de código y descomponerlo en pequeñas partes que vamos a probar. Así, todo lo que vamos pasando sabemos que esta funcionando correctamente y que no hay ningún inconveniente o bug.
Las pruebas unitarias comprueban lo que son casos estándares (suposición explícita) es decir, no son perfectas. Las características de las pruebas unitarias son:
- Automatizable: Deben correr sin ningún proceso manual.
- Total Cobertura: Debemos de pasar por cada bloque escrito.
- Reutilizables: Podemos usarlas para probar otros bloques.
- Independientes: No pueden depender de otra prueba para funcionar.
- Rápidas de crear: TIenen que ser algo conciso que prueben algo muy particular.
Ventajas de las pruebas unitarias:
- Proporciona un trabajo ágil.
- Calidad del código.
- Detectar errores rapido.
- Facilita los cambios y favorece la integración.
- Proporciona información.
- Reduce el coste.
https://github.com/dwyl/learn-tdd
Jest es una herramienta creada y mantenida por la comunidad open source con apoyo de Facebook. Jest surgió con el objetivo de agregar pruebas unitarias a proyectos con react pero ha sido tan importante que cada vez se emplea en mas proyectos con otros frameworks.
Jest no requiere configuraciones adicionales demasiado complejas para añadir pruebas a cualquiera de nuestros proyectos.
➜ yarn add -D jest @types/jest
➜ yarn add -D --exact @babel/core @babel/preset-env babel-jest @babel/polyfill
➜ mkdir __test__
- maths.js
export const sum = (a, b) => {
return a + b;
}
export const multiply = (a, b) => {
return a * b;
}
export const subtract = (a, b) => {
return a - b;
}
export const division = (a, b) => {
return a / b;
}
- __test__/maths.test.js
import { sum, multiply, subtract, division } from '../maths';
test('Sum test', () => {
expect(sum(1, 2)).toBe(3);
});
test('Multiply test', () => {
expect(multiply(4, 5)).toBe(20);
});
- package.json
"scripts": {
"test": "npx jest"
}
➜ ✗ yarn test
- __test__/matchers.test.js
describe('Common matchers', () => {
const user = {
name: "miguel",
lastName: "angel"
};
const user2 = {
name: "miguel2",
lastName: "angel2"
};
test("Element equal", () => {
expect(user).toEqual(user2);
});
test('Does not exactly complete', () => {
expect(user).not.toEqual(user2);
});
});
➜ yarn test matchers.test.js
- numbers.js
export const numbers = (a, b) => {
return a + b;
};
- __test__/numbers.test.js
import { numbers } from '../numbers';
describe('Number comparison', () => {
test('should greater than', () => {
expect(numbers(3, 2)).toBeGreaterThan(3);
});
test('should greater than or equal', () => {
expect(numbers(3, 2)).toBeGreaterThanOrEqual(5);
});
test('should less than', () => {
expect(numbers(3, 2)).toBeLessThan(6);
});
test('should less than or equal', () => {
expect(numbers(3, 2)).toBeLessThanOrEqual(5);
});
test('should float numbers', () => {
expect(numbers(2.3, 0.2)).toBeCloseTo(2.5);
});
});
➜ yarn test numbers.test.js
- true.js
export const isNull = () => null;
export const isTrue = () => true;
export const isFalse = () => false;
export const isUndefined = () => undefined;
- __test__/true.test.js
import { isNull, isTrue, isFalse, isUndefined } from '../true';
describe('Test null results', () => {
test('should be null', () => {
expect(isNull()).toBeNull();
});
});
describe('Test true results', () => {
test('should be true', () => {
expect(isTrue()).toBeTruthy();
});
});
describe('Test not true results', () => {
test('should not be true', () => {
expect(isFalse()).not.toBeTruthy();
});
});
describe('Test false results', () => {
test('should be false', () => {
expect(isFalse()).toBeFalsy();
});
});
describe('Test undefined results', () => {
test('should be undefined', () => {
expect(isUndefined()).toBeUndefined();
});
});
➜ yarn test true.test.js
- arrays.js
const fruits = ['banana', 'sandia', 'melón', 'naranja', 'limón', 'pepino'];
const colors = ['azul', 'verde', 'rojo', 'rosa', 'amarillo'];
export const arrayFruits = () => fruits;
export const arrayColors = () => colors;
- __test__/arrays.test.js
import { arrayFruits, arrayColors } from '../arrays';
describe('Check if exist an element', () => {
test('Contain a banana', () => {
expect(arrayFruits()).toContain('melón');
});
test('Not contain a pera', () => {
expect(arrayFruits()).not.toContain('pera');
});
test('Check array size', () => {
expect(arrayFruits()).toHaveLength(6);
});
});
➜ yarn test arrays.test.js
- package.json
"scripts": {
"test:coverage": "npx jest --coverage"
}
➜ yarn test:coverage
------------|----------|----------|----------|----------|-------------------|
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s |
------------|----------|----------|----------|----------|-------------------|
All files | 100 | 100 | 100 | 100 | |
arrays.js | 100 | 100 | 100 | 100 | |
maths.js | 100 | 100 | 100 | 100 | |
numbers.js | 100 | 100 | 100 | 100 | |
true.js | 100 | 100 | 100 | 100 | |
------------|----------|----------|----------|----------|-------------------|
open coverage/lcov-report/index.html file
- __test__/strings.test.js
describe('Test string texts', () => {
const text = 'A nice text';
test('should contain next text', () => {
expect(text).toMatch(/nice/);
});
test('should not contain next text', () => {
expect(text).not.toMatch(/beautiful/);
});
test('check string size', () => {
expect(text).toHaveLength(11);
});
});
➜ yarn test strings.test.js
- package.json
"scripts": {
"test:watch": "npx jest --watch"
}
➜ yarn test:watch
Test Suites: 6 passed, 6 total
Tests: 23 passed, 23 total
Snapshots: 0 total
Time: 1.976s, estimated 2s
Ran all test suites related to changed files.
Watch Usage
› Press a to run all tests.
› Press f to run only failed tests.
› Press p to filter by a filename regex pattern.
› Press t to filter by a test name regex pattern.
› Press q to quit watch mode.
› Press Enter to trigger a test run.
A menudo, mientras escribe pruebas, tiene que realizar algún trabajo de configuración antes de ejecutar las pruebas, y tiene que terminar algún trabajo después de la ejecución de las pruebas. Jest proporciona funciones de ayuda para manejar esto.
- __test__/setup.test.js
// After of each test
afterEach(() => console.log('After of each test'));
afterAll(() => console.log('After all of tests'));
// Before of each test
beforeEach(() => console.log('Before of each test'));
beforeAll(() => console.log('Before all of tests'));
describe('Prepare before execute', () => {
test('should be true', () => {
expect(true).toBeTruthy();
});
});
➜ yarn test setup.test.js
- callbacks.js
export const callbackHell = (callback) => {
callback('Hello JavaScript');
};
- __test__/callbacks.test.js
import { callbackHell } from '../callbacks';
describe('Test a Callback', () => {
test('Callback', done => {
function otherCallback(data) {
expect(data).toBe('Hello JavaScript');
done();
}
callbackHell(otherCallback);
});
});
➜ yarn test callbacks.test.js
➜ yarn add axios --exact
- promise.js
import axios from 'axios';
export const getDataFromApi = url => {
return axios.get(url)
.then(({ data }) => data)
.catch(error => error);
};
- __test__/promise.test.js
import { getDataFromApi } from '../promise';
describe('Test promises', () => {
test('Get data from API', () => {
const url = 'https://rickandmortyapi.com/api/character/';
return getDataFromApi(url)
.then(data => {
console.log(data.results.length);
expect(data.results.length).toBeGreaterThan(0);
});
});
});
➜ yarn test promise.test.js
- __test__/promise.test.js
describe('Test promises', () => {
// ...
test('Resolves an "hello"', () => {
return expect(Promise.resolve('hello')).resolves.toBe('hello');
});
test('Rejects with an error', () => {
return expect(Promise.reject('ERROR')).rejects.toBe('ERROR');
});
});
➜ yarn test promise.test.js
- __test__/async.test.js
import '@babel/polyfill';
import { getDataFromApi } from '../promise';
describe('Test Async/Await', () => {
test('Get data from API', async () => {
const url = 'https://rickandmortyapi.com/api/character/';
const data = await getDataFromApi(url);
expect(data.results.length).toBeGreaterThan(0);
const urlRick = 'https://rickandmortyapi.com/api/character/1';
const dataRick = await getDataFromApi(urlRick).then(data => data);
expect(dataRick.name).toEqual('Rick Sanchez');
});
});
➜ yarn test async.test.js
Las pruebas de instantáneas son una herramienta muy útil siempre que desee asegurarse de que su IU no cambie inesperadamente.
- rick.json
{
"id": 1,
"name": "Rick Sanchez",
"status": "Alive",
"species": "Human",
"gender": "Male"
}
- snapshot.js
export const getCharacter = data => ({
"id": data.id,
"name": data.name,
"status": data.status,
"species": data.species,
"gender": data.gender,
});
- __test__/snapshot.test.js
import { getCharacter } from '../snapshot';
import rick from '../rick.json';
describe('Snapshot test', () => {
test('Snapshot', () => {
expect(getCharacter(rick)).toMatchSnapshot();
});
test('Snapshot failure', () => {
const user = {
createdAt: new Date(),
id: Math.floor(Math.random() * 20),
};
expect(user).toMatchSnapshot();
});
test('Snapshot exception', () => {
const user = {
id: Math.floor(Math.random() * 20),
name: "Miguel Angel",
};
expect(user).toMatchSnapshot({
id: expect.any(Number),
});
});
});
➜ yarn test snapshot.test.js
➜ yarn test -u
https://github.com/gndx/platzi-react-jest
➜ yarn add -D enzyme enzyme-adapter-react-16 --exact
- platzi-react-jest/src/__test__/Hello.test.js
import React from 'react';
import Enzyme, { mount, shallow } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import Hello from '../components/Hello';
Enzyme.configure({ adapter: new Adapter() });
describe('Test React', () => {
test('check the text than prop received', () => {
const data = { title: 'Hello Jest' };
const wrapper = mount(
<Hello title={data.title} />
);
const h1 = wrapper.find('h1');
expect(h1.text()).toBe('Hello Jest');
});
});
➜ yarn test
https://github.com/gndx/platzi-vue-jest
- platzi-vue-jest/test/unit/jest.conf.js
module.exports = {
testURL: 'http://localhost',
}
- platzi-vue-jest/test/unit/specs/HelloWorld.spec.js
import Vue from 'vue'
import HelloWorld from '@/components/HelloWorld'
describe('HelloWorld.vue', () => {
it('should render correct contents', () => {
const Constructor = Vue.extend(HelloWorld)
const vm = new Constructor().$mount()
expect(vm.$el.querySelector('.hello h1').textContent)
.toEqual('Hello Vue :)')
});
test('Snapshot Vue.js', () => {
const Constructor = Vue.extend(HelloWorld);
const vm = new Constructor().$mount()
expect(vm.$el).toMatchSnapshot();
});
})
➜ yarn test
https://github.com/gndx/platzi-express-jest
➜ yarn add -D @babel/core @babel/cli @babel/preset-env jest supertest --exact
- platzi-express-jest/__test__/app.test.js
const request = require('supertest');
const app = require('../app');
describe('Express.js test', () => {
test('should response to the GET method', done => {
request(app).get('/').then(response => {
expect(response.statusCode).toBe(200);
done();
});
});
});
➜ npx jest
PASS __test__/app.test.js
Express.js test
✓ should response to the GET method (29ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 1.311s, estimated 2s
Ran all test suites.