Todos sabemos que deberíamos escribir pruebas unitarias. Pero es difícil saber por dónde empezar y cuánto tiempo dedicar a las pruebas en comparación con la implementación real. Entonces, ¿por dónde empezar? ¿Y se trata solo de probar el código o las pruebas unitarias tienen otros beneficios?
En este artículo, explicaré los diferentes tipos de pruebas y qué beneficios aportan las pruebas unitarias a los equipos de desarrollo. Mostraré Jest, un marco de prueba de JavaScript.
Diferentes tipos de pruebas
Antes de sumergirnos en los detalles específicos de las pruebas unitarias, quiero hacer un repaso rápido de los diferentes tipos de pruebas. A menudo hay cierta confusión en torno a ellos y no me sorprende. A veces, la línea entre ellos es bastante delgada.
Pruebas unitarias
Las pruebas unitarias solo prueban una sola parte de su implementación. Una unidad. Sin dependencias o integraciones, sin especificaciones del marco. Son como un método que devuelve un enlace en un idioma específico:
export function getAboutUsLink(language){ switch (language.toLowerCase()){ case englishCode.toLowerCase(): return '/about-us'; case spanishCode.toLowerCase(): return '/acerca-de'; } return ''; }
Pruebas de integración
En algún momento, su código se comunica con una base de datos, un sistema de archivos u otro tercero. Incluso podría ser otro módulo de su aplicación.
Esa parte de la implementación debe probarse mediante pruebas de integración. Por lo general, tienen una configuración más complicada que implica preparar entornos de prueba, inicializar dependencias, etc.
Pruebas funcionales
Las pruebas unitarias y las pruebas de integración le brindan la confianza de que su aplicación funciona. Las pruebas funcionales analizan la aplicación desde el punto de vista del usuario y comprueban que el sistema funciona como se esperaba.

En el diagrama anterior, puede ver que las pruebas unitarias forman la gran base del conjunto de pruebas de su aplicación. Por lo general, son pequeños, hay muchos y se ejecutan automáticamente.
Así que ahora veamos las pruebas unitarias con un poco más de detalle.
¿Por qué debería molestarme en escribir pruebas unitarias?
Siempre que les pregunto a los desarrolladores si escribieron pruebas para su aplicación, siempre me dicen: "No tuve tiempo para ellos" o "No los necesito, sé que funciona".
Así que sonrío cortésmente y les digo lo que quiero decirte. Las pruebas unitarias no son solo pruebas. También le ayudan de otras formas, para que pueda:
Tenga la seguridad de que su código funciona. ¿Cuándo fue la última vez que cometió un cambio de código, su compilación falló y la mitad de su aplicación dejó de funcionar? El mío fue la semana pasada.
Pero eso sigue estando bien. El problema real es cuando la compilación tiene éxito, se implementa el cambio y su aplicación comienza a ser inestable.
Cuando eso sucede, comienzas a perder la confianza en tu código y, finalmente, solo rezas para que la aplicación funcione. Las pruebas unitarias le ayudarán a descubrir problemas mucho antes y a ganar confianza.
Tome mejores decisiones arquitectónicas. El código cambia, pero algunas decisiones sobre la plataforma, los módulos, la estructura y otras deben tomarse durante las primeras etapas de un proyecto.
Cuando comience a pensar en las pruebas unitarias desde el principio, lo ayudará a estructurar mejor su código y lograr una separación adecuada de preocupaciones. No tendrá la tentación de asignar múltiples responsabilidades a bloques de código único, ya que sería una pesadilla para la prueba unitaria.
Identifique la funcionalidad antes de codificar. Escribe la firma del método y comienza a implementarlo de inmediato. Oh, pero ¿qué debería suceder en caso de que un parámetro sea nulo? ¿Qué pasa si su valor está fuera del rango esperado o contiene demasiados caracteres? ¿Lanza una excepción o devuelve nulo?
Las pruebas unitarias le ayudarán a descubrir todos estos casos. Mire las preguntas nuevamente y encontrará que es exactamente lo que define sus casos de prueba unitaria.
Estoy seguro de que escribir pruebas unitarias tiene muchos más beneficios. Estos son solo los que recuerdo de mi experiencia. Los que aprendí por las malas.
Cómo escribir su primera prueba unitaria de JavaScript
Pero volvamos a JavaScript. Comenzaremos con Jest, que es un marco de prueba de JavaScript. Es una herramienta que permite realizar pruebas unitarias automáticas, proporciona cobertura de código y nos permite simular objetos fácilmente. Jest también tiene una extensión para Visual Studio Code disponible aquí.
También hay otros frameworks, si está interesado, puede consultarlos en este artículo.
npm i jest --save-dev
Usemos el método mencionado anteriormente getAboutUsLink
como una implementación que queremos probar:
const englishCode = "en-US"; const spanishCode = "es-ES"; function getAboutUsLink(language){ switch (language.toLowerCase()){ case englishCode.toLowerCase(): return '/about-us'; case spanishCode.toLowerCase(): return '/acerca-de'; } return ''; } module.exports = getAboutUsLink;
Pongo esto en el index.js
archivo. Podemos escribir pruebas en el mismo archivo, pero una buena práctica es separar las pruebas unitarias en un archivo dedicado.
Los patrones de nomenclatura comunes incluyen {filename}.test.js
y {filename}.spec.js
. Usé el primero index.test.js
,:
const getAboutUsLink = require("./index"); test("Returns about-us for english language", () => { expect(getAboutUsLink("en-US")).toBe("/about-us"); });
Primero, necesitamos importar la función que queremos probar. Cada prueba se define como una invocación de la test
función. El primer parámetro es el nombre de la prueba para su referencia. La otra es una función de flecha donde llamamos a la función que queremos probar y especificamos qué resultado esperamos. yo
En este caso, llamamos a la getAboutUsLink
función con en-US
como parámetro de idioma. Esperamos que el resultado sea /about-us
.
Ahora podemos instalar la CLI de Jest globalmente y ejecutar la prueba:
npm i jest-cli -g jest
Si ve un error relacionado con la configuración, asegúrese de tener su package.json
archivo presente. En caso de que no lo haga, genere uno usando npm init
.
Debería ver algo como esto:
PASS ./index.test.js √ Returns about-us for english language (4ms) console.log index.js:15 /about-us Test Suites: 1 passed, 1 total Tests: 1 passed, 1 total Snapshots: 0 total Time: 2.389s
¡Gran trabajo! Esta fue la primera prueba unitaria simple de JavaScript de principio a fin. Si instaló la extensión de Visual Studio Code, ejecutará pruebas automáticamente una vez que guarde un archivo. Intentémoslo ampliando la prueba con esta línea:
expect(getAboutUsLink("cs-CZ")).toBe("/o-nas");
Una vez que guarde el archivo, Jest le informará que la prueba falló. Eso le ayuda a descubrir problemas potenciales incluso antes de realizar los cambios.
Prueba de funcionalidad avanzada y servicios de simulación
En la vida real, los códigos de idioma para el método getAboutUsLink no serían constantes en el mismo archivo. Su valor se utiliza normalmente en todo el proyecto, por lo que se definirían en su propio módulo y se importarían a todas las funciones que los utilizan.
import { englishCode, spanishCode } from './LanguageCodes'
Puede importar estas constantes a la prueba de la misma manera. Pero la situación se complicará más si trabaja con objetos en lugar de simples constantes. Eche un vistazo a este método:
import { UserStore } from './UserStore' function getUserDisplayName(){ const user = UserStore.getUser(userId); return `${user.LastName}, ${user.FirstName}`; }
Este método utiliza importados UserStore
:
class User { getUser(userId){ // logic to get data from a database } setUser(user){ // logic to store data in a database } } let UserStore = new User(); export { UserStore }
Para probar correctamente este método, necesitamos simularlo UserStore
. Un simulacro es un sustituto del objeto original. Nos permite separar dependencias y datos reales de la implementación del método probado, al igual que los maniquíes ayudan con las pruebas de choque de automóviles en lugar de personas reales.
Si no usáramos el simulacro, estaríamos probando tanto esta función como la tienda. Esa sería una prueba de integración y probablemente tendríamos que simular la base de datos utilizada.
Burlarse de un servicio
To mock objects, you can either provide a mocking function or a manual mock. I will focus on the latter as I have a plain and simple use-case. But feel free to check out other mocking possibilities Jest provides.
jest.mock('./UserStore', () => ({ UserStore: ({ getUser: jest.fn().mockImplementation(arg => ({ FirstName: 'Ondrej', LastName: 'Polesny' })), setUser: jest.fn() }) }));
First, we need to specify what are we mocking - the ./UserStore
module. Next, we need to return the mock that contains all exported objects from that module.
In this sample, it's only the User
object named UserStore
with the function getUser
. But with real implementations, the mock may be much longer. Any functions you don't really care about in the scope of unit testing can be easily mocked with jest.fn()
.
The unit test for the getUserDisplayName
function is similar to the one we created before:
test("Returns display name", () => { expect(getUserDisplayName(1)).toBe("Polesny, Ondrej"); })
As soon as I save the file, Jest tells me I have 2 passing tests. If you're executing tests manually, do so now and make sure you see the same result.
Code Coverage Report
Now that we know how to test JavaScript code, it's good to cover as much code as possible with tests. And that is hard to do. In the end, we're just people. We want to get our tasks done and unit tests usually yield an unwanted workload that we tend to overlook. Code coverage is a tool that helps us fight that.
Code coverage will tell you how big a portion of your code is covered by unit tests. Take for example my first unit test checking the getAboutUsLink
function:
test("Returns about-us for english language", () => { expect(getAboutUsLink("en-US")).toBe("/about-us"); });
It checks the English link, but the Spanish version stays untested. The code coverage is 50%. The other unit test is checking the getDisplayName
function thoroughly and its code coverage is 100%. Together, the total code coverage is 67%. We had 3 use cases to test, but our tests only cover 2 of them.
To see the code coverage report, type the following command into the terminal:
jest --coverage
Or, if you're using Visual Studio Code with the Jest extension, you can run the command (CTRL+SHIFT+P) Jest: Toggle Coverage Overlay. It will show you right in the implementation which lines of code are not covered with tests.

By running the coverage check, Jest will also create an HTML report. Find it in your project folder under coverage/lcov-report/index.html
.

Now, I don't have to mention that you should strive for 100% code coverage, right? :-)
Summary
In this article, I showed you how to start with unit testing in JavaScript. While it's nice to have your code coverage shine at 100% in the report, in reality, it's not always possible to (meaningfully) get there. The goal is to let unit tests help you maintain your code and ensure it always works as intended. They enable you to:
- clearly define implementation requirements,
- better design your code and separate concerns,
- discover issues you may introduce with your newer commits,
- and give you confidence that your code works.
The best place to start is the Getting started page in the Jest documentation so you can try out these practices for yourself.
Do you have your own experience with testing code? I'd love to hear it, let me know on Twitter or join one of my Twitch streams.