Revisión de código es la examinación sistemática (como en la revisión por pares) del código fuente de un programa informático. Se practica con el objetivo de mejorar la calidad del código que se genera en el proceso de desarrollo del software, mediante la detección temprana de errores en el código de los programas o alternativas más eficientes a la implementación inicial. También se utiliza como técnica para mejorar las cualidades de los desarrolladores involucrados en la práctica, mediante la discusión abierta de posibles mejoras en el programa.
Se realizan principalmente revisiones de código por parte de las metodologías ágiles que practican programación en pareja como parte del proceso, o en otras que pueden implementar revisiones periódicas de código, tanto informales como formales. Wikipedia
Las claves
- Forma parte de algunas metodologías ágiles
- Mejora la comunicación y la calidad del grupo
- No culpar a nadie, ni tomarselo personal
- La culpa de un bug es ahora compartida
- Se sugieren los cambios y se discuten
¿Que revisar?
- Arquitectura/Diseño (patrones, errores potenciales, manejo de errores, eficiencia...)
- Estilo (Longitudes, legibilidad, nombres...)
- Testing (Cobertura, mocks, etc...)
Herramientas
La Programación en Pareja (o Pair Programming en inglés) requiere que dos programadores participen en un esfuerzo combinado de desarrollo en un sitio de trabajo. Cada miembro realiza una acción que el otro no está haciendo actualmente: Mientras que uno codifica las pruebas de unidades el otro piensa en la clase que satisfará la prueba, por ejemplo.
La persona que está haciendo la codificación se le da el nombre de controlador mientras que a la persona que está dirigiendo se le llama el navegador. Se sugiere a menudo para que a los dos socios cambien de papeles por lo menos cada media hora o después de que se haga una prueba de unidad. Wikipedia
Lo bueno
- Mejor código
- Equipo más fuerte y cohesionado
- Se aprende mucho más
- Mejora la propiedad colectiva del código
- Mejora las habilidades sociales
- Reduce el número de bugs
Lo malo
- No todo el mundo se siente cómodo
- No es facil juntar niveles distintos
- Es dificil combinar tiempos (balance, vida personal, etc....)
- Es más dificil en remoto
- Los tiempos de desarrollo son distintos
Las claves
- Persona especializada en asegurar la calidad del proyecto
- Se encarga del diseño y ejecucción de pruebas
- Se encarga de validar la caldiad técnica (rendimeinto, optimización, etc...)
- Define standares, medidas y metricas que debemos cumplir
- Revisa y mantiene seguimiento de la calidad
- Coordina el testeo
- Las funciones pueden cambiar entre proyectos y empresas
Recursos
- ¿Qué es QA y por qué no debe faltar en tu proyecto?
- ¿Que es un QA Tester?
- Tester vs Quality Assurance
- Wikipedia | Aseguramiento de la calidad
- Qué es QA
- Quality Assurance no es sólo Testing
- La misión del QA Manager dentro de la Organización
- QA (Quality Assurance) y su mundo
Las pruebas de software (en inglés software testing) son las investigaciones empíricas y técnicas cuyo objetivo es proporcionar información objetiva e independiente sobre la calidad del producto a la parte interesada o stakeholder. Es una actividad más en el proceso de control de calidad. Wikipedia
Tipos de pruebas
- Estáticas, No necesitan ejecutar código alguno
- Dinámicas, que requieren ejecucción de código
Según su ejecucción
- Manuales, requieren de nuestra interacción para funcionar
- Automáticas, la propia máquina es capaz de relaizar las pruebas sin sufrir alteraciones
Según el enfoque
- Caja Blanca, nos centramos en el funcionamiento interno de las cosas
- Caja Negra, nos centramos en las entradas (input) y salidas (output) de las clsas y no en su funcionamiento
- Pruebas aleatorias, variante de la caja negra donde el input es aletorio
- Pruebas unitarias (Unit Testing)
- Pruebas de integración (Integration testing)
- Pruebas de sistema (System testing)
- Pruebas de sanidad (Sanity check)
- Pruebas de humo (Smoke testing)
- Pruebas alpha (Alpha Testing)
- Pruebas beta (Beta Testing)
- Pruebas de aceptación (Acceptance Testing)
- Pruebas de regresión (Regression testing)
- Pruebas de compatibilidad
- Pruebas de Accesibilidad (Accessibility testing)
- Pruebas de seguridad (Security Testing)
- Pruebas de destrucción (Destructive testing)
- Pruebas de Stress (Stress Testing)
- Pruebas de Carga (Load Testing)
- Pruebas de usabilidad (Usability testing)
- Pruebas de rendimiento (Performance Testing)
- Pruebas de internacionalización y localización
- Pruebas de escalabilidad
- A/B testing
- Pruebas de concurrencia (Concurrent testing)
- Prueba de conformidad (Conformance testing)
- Unit Testing
- Integration testing
- End-To-End Testing (e2e)
- Alpha & Beta Testing
- Accessibility testing
- Security Testing
- Stress Testing & Load Testing
- A/B testing
Desarrollo guiado por pruebas de software, o Test-driven development (TDD) es una práctica de ingeniería de software que involucra otras dos prácticas: Escribir las pruebas primero (Test First Development) y Refactorización (Refactoring). Para escribir las pruebas generalmente se utilizan las pruebas unitarias (unit test en inglés). En primer lugar, se escribe una prueba y se verifica que las pruebas fallan. A continuación, se implementa el código que hace que la prueba pase satisfactoriamente y seguidamente se refactoriza el código escrito. El propósito del desarrollo guiado por pruebas es lograr un código limpio que funcione. La idea es que los requisitos sean traducidos a pruebas, de este modo, cuando las pruebas pasen se garantizará que el software cumple con los requisitos que se han establecido. Wikipedia
Las claves
- Implementar solo lo necesario
- Evitar bugs a toda costa
- Creamos software modular y reutilizable
- No tener miedo de tocar "legacy"
El Ciclo de desarrollo
- Elegir un requisito
- Escribir las pruebas
- Verificar que fallan las pruebas
- Escribir suficiente código para pasar las preubas
- Pasar las pruebas
- Refactorizar y pasar las pruebas (hasta estar listo)
- El requisito ha sido implementado
Filosofías derivadas
Recursos
- ¿Cuál es la diferencia entre Unit Testing, TDD y BDD?
- BDD + TDD para descubrir el diseño de tu código
- TDD, BDD & Test de Aceptación
- TDD vs BDD vs ATDD
- BDD y TDD en el mundo real (I) – Metodologías y herramientas
- BDD y TDD en el mundo real (II) – Ciclo desde la fuente
- The Difference Between TDD and BDD
- Orígenes de TDD, BDD, ATDD y sus diferencias
- Wikipedia | Desarrollo guiado por comportamiento
- ¿ATDD? ¿BDD?… ¿Cómo? Aclarando el lío de siglas en Testing
- Youtube | BDD vs TDD (explained)
- De testers y code reviews
Las claves
- Código que verifica el funcionamiento de otro código.
- Deben poder realizarse de manera automática.
- Cubrir mayor cantidad de código posible.
- Independientes entre si.
- Capaces de ejercutarse infinidad de veces.
- Pueden agruparse en Test Suites.
- Uso de colores y mensajes claros.
- A guide to unit testing in JavaScript
Ejemplo: Versión Browser
// Función
function sumar (p1, p2){
return p1 + p2;
}
// Test
function testSumar(){
if (sumar(1, 2) !== 3) {
document.write('<p style="color: red;">sumar(1, 2) ERROR - No devuelve 3</\p>');
} else {
document.write('<p style="color: green;">sumar(1, 2) OK</p>');
}
if (sumar("2", 2) !== 4) {
document.write('<p style="color: red;">sumar("2", 2) ERROR - No devuelve 4</p>');
} else {
document.write('<p style="color: green;">sumar("2", 2) OK</p>');
}
}
Ejemplo: Versión Node.js
const chalk = require('chalk');
const log = console.log;
// Función
function sumar (p1, p2){
return p1 + p2;
}
// Test
function trueAssert(msg) {
log(chalk.bgGreen.white(msg))
}
function falseAssert(msg) {
log(chalk.bgRed.white(msg))
}
function testSumar(){
if (sumar(1, 2) !== 3) {
falseAssert("sumar(1, 2) ERROR")
} else {
trueAssert("sumar(1, 2) OK")
}
if (sumar("2", 2) !== 4) {
falseAssert('sumar("2", 2) ERROR - No devuelve 4')
} else {
trueAssert('sumar("2", 2) OK')
}
}
testSumar();
const controlador = false;
console.assert(controlador, "\"controlador\" es igual a \"false\"");
const assert = require('assert');
assert.equal(1, 1); // OK, 1 == 1
assert.equal(1, '1'); // OK, 1 == '1'
assert.equal(1, 2); // AssertionError: 1 == 2
assert.equal({ a: { b: 1 } }, { a: { b: 1 } });
// AssertionError: { a: { b: 1 } } == { a: { b: 1 } }
- Strict mode
assert(value[, message])
assert.deepEqual(actual, expected[, message])
assert.deepStrictEqual(actual, expected[, message])
assert.deepStrictEqual
: Comparison detailsassert.doesNotReject(block[, error][, message])
assert.doesNotThrow(block[, error][, message])
assert.equal(actual, expected[, message])
assert.fail([message])
assert.ifError(value)
assert.notDeepEqual(actual, expected[, message])
assert.notDeepStrictEqual(actual, expected[, message])
assert.notEqual(actual, expected[, message])
assert.notStrictEqual(actual, expected[, message])
assert.ok(value[, message])
assert.rejects(block[, error][, message])
assert.strictEqual(actual, expected[, message])
assert.throws(block[, error][, message])
BDD/TDD
- MochaJS - ☕️ simple, flexible, fun javascript test framework for node.js & the browser
- Jasmine - Simple JavaScript testing framework for browsers and node.js
- Intern - Intern. Software testing for humans.
- Chai- BDD / TDD assertion framework for node.js and the browser that can be paired with any testing framework.
- Ava - 🚀 Futuristic JavaScript test runner
BDD
- CucumberJS - Cucumber for JavaScript
- Unexpected - The extensible BDD assertion toolkit
- Karma - Spectacular Test Runner for JavaScript
- Jest - 🃏 Delightful JavaScript Testing.
- GlaceJS - glace-core is a quick-start functional & unit testing framework based on mocha and extensible with plugins (how plugins work).
- Apickli - apickl. REST API integration testing framework based on cucumber.js
- Sinon.JS - Test spies, stubs and mocks for JavaScript.
- JSMockito - Javascript mocking framework inspired by the awesome mockito
- Apimocker - node.js module to run a simple http server for mock service responses.
- Rewire - Easy monkey-patching for node.js unit tests
- Enzyme - JavaScript Testing utilities for React
- Testdouble - A minimal test double library for TDD with JavaScript
- ESLint - A fully pluggable tool for identifying and reporting on patterns in JavaScript
- JsHint - JSHint is a tool that helps to detect errors and potential problems in your JavaScript code
- JsLint - The JavaScript Code Quality Tool
- k6 - A modern load testing tool, using Go and JavaScript
- Artillery - Flexible and powerful toolkit for load and functional testing. HTTP, Socket.io, WebSockets, Kinesis, HLS. Make your systems indestructible! 👩💻 🏰
- LoadComplete - Load Testing Tool for Websites and Web Apps
- OWASP Glue - Application Security Automation
- OWASP ZAP - The OWASP ZAP core project
- BeEF - Manipulate the browser exploiting any XSS vulns you find
- ReportPortal.io - AI-powered Test Automation Dashboard
- Istanbul - Yet another JS code coverage tool that computes statement, line, function and branch coverage with module loader hooks to transparently add coverage when running tests. Supports all JS coverage use cases including unit tests, server side functional tests and browser tests. Built for scale.
- Blanket - blanket.js is a simple code coverage library for javascript. Designed to be easy to install and use, for both browser and nodejs.
- Mochawesome - A Gorgeous HTML/CSS Reporter for Mocha.js
- allure - There are lots of cool testing frameworks for different programming languages. Unfortunately only a few of them can provide good representation of test execution output. The Yandex testing team is working on Allure - an open-source framework designed to create test execution reports that are clear to everyone in the team.
Las Claves
- Facil y sencillo
- Ideal para empezar
- No contiene muchos plugins
- Originalmente creado para testear JQuery
Implementacion en browser
fichero index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>QUnit Example</title>
<link rel="stylesheet" href="https://code.jquery.com/qunit/qunit-2.6.2.css">
</head>
<body>
<div id="qunit"></div>
<div id="qunit-fixture"></div>
<script src="https://code.jquery.com/qunit/qunit-2.6.2.js"></script>
<script src="tests.js"></script>
</body>
</html>
fichero test.js
QUnit.test( "hello test", function( assert ) {
assert.ok( 1 == "1", "Passed!" );
});
Implementación en Nodejs
npm install --save-dev qunit
Ejemplo
// Función
function sumar (p1, p2){
return p1 + p2;
}
// Test
test("test de la funcion sumar(p1, p2)", function() {
equal(sumar( 1, 2), 3, "1 + 2 = 3" );
notEqual(sumar( "2", 2), "4", " 2(cadena) + 2 != 4(cadena) ");
});
Documentación
- Web Oficial
- Documentación
- Introduction to Unit Testing
- QUnit en Github
- El equipo
- @qunitjs en Twitter
- QUnit Forum
- QUnit Cookbook
QUnit.module( name [, hooks] [, nested ] )
QUnit.only( name, callback )
QUnit.skip( name )
QUnit.start()
QUnit.test( name, callback )
QUnit.todo( name, callback )
async( [ acceptCallCount = 1 ] )
deepEqual( actual, expected [, message ] )
equal( actual, expected [, message ] )
expect( amount )
notDeepEqual( actual, expected [, message ] )
notEqual( actual, expected [, message ] )
notOk( state [, message ] )
notPropEqual( actual, expected [, message ] )
notStrictEqual( actual, expected [, message ] )
ok( state [, message ] )
propEqual( actual, expected [, message ] )
pushResult( data: { result, actual, expected, message } )
rejects( promise[, expectedMatcher][, message ] )
step( [ message ] )
strictEqual( actual, expected [, message ] )
throws( blockFn[, expectedMatcher][, message ] )
timeout( duration )
verifySteps( steps [, message ] )
QUnit.assert
QUnit.config
QUnit.dump.parse( data )
QUnit.extend( target, mixin )
QUnit.push( result, actual, expected, message )
QUnit.stack( [ offset = 0 ] )
QUnit.begin( callback )
QUnit.done( callback )
QUnit.log( callback )
QUnit.moduleDone( callback )
QUnit.moduleStart( callback )
QUnit.on( eventName, callback )
QUnit.testDone( callback )
QUnit.testStart( callback )
Las claves
- Mocha es el framework de testing isomórfico
- Chai es la librería de aserciones que ofrece tres interfaces (assert, expect y shpuld)
Las interfaces de Chai
//Assert:
assert.typeOf( cadena, 'string', 'cadena es un string' );
//Expect:
expect( cadena ).to.be.a( 'string' );
//Should:
cadena.should.be.a( 'string' );
Chai plugins
- chai-semver - Semver plugin for Chai
- chai-fs - Chai assertion plugin for the Node.js filesystem API. Uses
path
and synchronousfs
to assert files and directories. - Chai Events - Make assertions about event emitters.
- Chai HTTP - HTTP integration testing with Chai assertions.
- chai-spy - 🔎 Spies for the Chai Assertion Library
- Chai Assertions for RxJS Observables - ChaiRx extends Chai with a simple utility method
emit
for testing emits from an RxJS Observable stream using theRx.TestScheduler
. - chai-moment-string - chai plugin for validating a string with an expected moment format
- chai-fireproof - Chai assertions and helpers for Firebase and Fireproof.
- chai-eventemitter - This is a plugin for chai to simplify the testing of EventEmitter.
- Chai Spies - It provides the most basic function spy ability and tests.
- Sinon.JS Assertions for Chai - Sinon–Chai provides a set of custom assertions for using the Sinon.JS spy, stub, and mocking framework with the Chai assertion library. You get all the benefits of Chai with all the powerful tools of Sinon.JS.
- chai-url - A chai assertion plugin for working with urls
- chai-dom - chai-dom is an extension to the chai assertion library that provides a set of assertions when working with the DOM (specifically HTMLElement and NodeList)
- In-Order Sinon-Chai Assertions - Sinon-Chai provides Chai assertions for Sinon.JS. Unfortunately, it does not deal with making sure a spy was called multiple time in a specific order.
Ejemplo
// Función
function sumar (p1, p2){
return p1 + p2;
}
// Test
mocha.setup('bdd');
const expect = chai.expect;
const should = chai.should();
describe("Test de la funcion sumar(p1, p2)", () => {
it('1 + 2 = 3', () => {
expect(sumar( 1, 2)).to.equal(3);
});
it('\"2\" + 2 != \"4\"', () => {
expect(sumar( "2", 2)).not.equal("4");
});
});
mocha.run();
Documentación
Documentación
Ejemplo
// Función
function getTodos(listId, callback) {
jQuery.ajax({
url: '/todo/' + listId + '/items',
success: function (data) {
// Node-style CPS: callback(err, data)
callback(null, data);
}
});
}
// Testing
after(function () {
// When the test either fails or passes, restore the original
// jQuery ajax function (Sinon.JS also provides tools to help
// test frameworks automate clean-up like this)
jQuery.ajax.restore();
});
it('makes a GET request for todo items', function () {
sinon.replace(jQuery, 'ajax', sinon.fake());
getTodos(42, sinon.fake());
assert(jQuery.ajax.calledWithMatch({ url: '/todo/42/items' }));
});
Recursos
- A guide to unit testing in JavaScript
- How Netflix does A/B Testing
- An Overview of JavaScript Testing in 2018
- I Spent $30,000 Testing Different Blog Designs — Here’s What I Found
- An Overview of JavaScript Testing in 2017
- Airbnb Guerilla Usability Testing
- A crash course on testing with Node.js
- The easy way to start automatically testing your website
- Why End-to-End Testing is Important for Your Team
- JS Vanilla Test Code Coverage
- Vanilla JS Testing — Part II
- AVA, low-config testing for JavaScript
- Revisiting Node.js Testing: Part 1
- How to test JavaScript with Mocha — The Basics
- Node.js End-to-End Testing with Nightwatch.js
- How to automate tests and deployments of Node.js apps
- Github | TheJambo/awesome-testing
- Github | atinfo/awesome-test-automation
- Github | ligurio/awesome-software-quality
- 7 Things Awesome Testers do That Don't Look Like Testing
- 5 Awesome Mobile Application Testing Tools & Platforms for Testers
- Software Testing for Continuous Delivery
- API Unit Testing with Node JS
- Node Hero - Node.js Unit Testing Tutorial
- Unit Testing and TDD in Node.js – Part 1
- JavaScript — Unit Testing using Mocha and Chai
- Lo mejor y peor de Mocha y de los unit tests en JavaScript
- How to mock a dependency in a Node.js, and why you should do it.
- How to mock requests for unit testing in Node
- Using Jasmine to Test Node.js Applications
- How to correctly unit test Express server
- Node.js Unit Testing Tutorial with Jasmine
- Testing Node.js with Mocha and Chai
- Getting Started with Node.js and Mocha
- Github | unicodeveloper/awesome-tdd
- 8 Reasons Why Unit Tests are Awesome
- The No. 1 unit testing best practice: Stop doing it
- Unit Tests, How to Write Testable Code and Why it Matters
Librerías Clásicas
- WebDriverJs - WebDriverJs is the Official javascript implementation of selenium. It uses to Selenium’s Json-wire-protocol to interact with browser. It is written by selenium guys.
- WebdriverIO - Webdriver IO is a selenium wrapper written in javascript and runs on node.js. It is a custom implementation of W3C webdriver protocol.
- Protractor - Protractor is an End-to-End test framework for Angular Js applications. It is packaged under npm and runs on NodeJs.
- Nightwatch.js - Nightwatch.js is a Node.js based End-to-End (E2E) testing solution. It uses the powerful W3C WebDriver API to perform actions on DOM elements.
- Dalek.js - Automated cross browser testing with JavaScript
- Nemo - Launch your web sites & applications with confidence
- TestCafe - A node.js tool to automate end-to-end web testing
- Nightmare - A high-level browser automation library.
- Hermione - Browser test runner based on mocha and wdio
- Chromeless - 🖥 Chrome automation made simple. Runs locally or headless on AWS Lambda.
- Chrominator - high level api to the chrome remote debugger
- Chromy - Chromy is a library for operating headless chrome. 🍺🍺🍺
- Navalia - A bullet-proof, fast, and reliable headless browser API
- Lambdium - headless chrome + selenium webdriver in AWS Lambda using the serverless application model
- GhostJS - ghostjs provides modern UI testing with async functions
- CasperJS - Navigation scripting & testing for PhantomJS and SlimerJS
- Cypress.io - Fast, easy and reliable testing for anything that runs in a browser.
Recursos
- End-to-End Testing With Puppeteer
- End-to-end Tests that Don’t Suck with Puppeteer
- Unit test vs. Integration test
Uso soluciones clásicas (Selenium, etc...)?
- Necesito testear en más de un navegador
- Necesito testear en un único navegador pero no es Chrome
Uso Puppeteer?
Librerias y utlilidades
- Supertest - Super-agent driven library for testing node.js HTTP servers using a fluent API
- Faker.js - generate massive amounts of fake data in Node.js and the browser
- Frisby - The Easiest REST API Testing Framework Out There
- Gemini - Gemini is a utility for regression testing the visual appearance of web pages.
- Gremlins.js - Monkey testing library for web apps and Node.js
- Mailosaur - Mailosaur Node.js Client Library
- Differencify - Differencify is a library for visual regression testing
- CodeceptJS - Modern era acceptance testing for Nodejs
Librerias
- Benchmark.js - Benchmarking library that supports high-resolution timers and returns statistically significant results.
- matcha - Simplistic approach to benchmarking.
Ejemplo
const suite = new Benchmark.Suite;
// add tests
suite.add('RegExp#test', () => {
/o/.test('Hello World!');
})
.add('String#indexOf', () => {
'Hello World!'.includes('o');
})
.add('String#match', () => {
!!'Hello World!'.match(/o/);
})
// add listeners
.on('cycle', event => {
console.log(String(event.target));
})
.on('complete', function() {
console.log(`Fastest is ${this.filter('fastest').pluck('name')}`);
})
// run async
.run({ 'async': true });
Librerías
Librerías
1 - Inicializa un repositorio utilizando npm y git. Deberás preparar un entorno básico de testing siguiendo los siguientes patrones:
- Crea un directorio
lib/
donde irá el código de tu proyecto. - Crea un directorio
tests/
donde irán las pruebas unitarias. - En el
package.json
se debe crear el scripttest
para que se ejecuten los tests del proyecto. - Cuando ejecutes los tests (utilizando
mocha + chai
) se deben ejecutar todos los ficheros en el directoriotests/
.
2 - Aplicando TDD y utilizando el repositorio del ejercicio anterior, crea una función atm
que devuelva el número más eficiente de billetes dados los siguientes casos:
- Si un valor no es válido (ej. negativo) no debe devolver nada.
- La función debe devolver billetes de entre 5 y 50€.
Ejemplo (5€)
- 1 billete de 5€.
Ejemplo (35€)
- 1 billete de 5€.
- 1 billete de 10€.
- 1 billetes de 20€.
Ejemplo (165€)
- 1 billete de 5€.
- 1 billete de 10€.
- 3 billetes de 50€.
3 - Crea una función que acepte como primer argumento un número.
Esa función debe devolver un objeto con los métodos add()
y subtract()
, y a su vez esos métodos se podrán ir encadenando hasta el infinito.
- Cuando se llame a la función
add()
, tendrá que imprimir por consola el número anterior+1
. - Cuando se llame a la función
subtract()
, tendrá que imprimir por consola el número anterior-1
.
Ejemplo:
function createNumber() {
// return ...
}
createNumber(5) // prints "5"
.add() // prints "6"
.add() // prints "7"
.subtract() // prints "6"
.add() // prints "7"
// ...
4 - Testea la siguiente función utilizando sinon.js
y sinon-chai:
function getFilm(id, adapter) {
const url = `https://ghibliapi.herokuapp.com/films/${id}`;
if (!id) {
throw new Error('ID not exists');
}
return adapter(url).then((json) => {
return json.title;
});
}