Skip to content

Latest commit

 

History

History
287 lines (228 loc) · 19.7 KB

README_es.md

File metadata and controls

287 lines (228 loc) · 19.7 KB

Testy

ci
maintainability tech-debt coverage Mutation testing badge
Maintainability Rating Technical Debt Reliability Rating Security Rating
GitHub Repo stars open-issues closed-issues open-prs
downloads dependencies
package-size activity release-date \

all-contributors

Una simple herramienta de testeo en Javascript, para propósitos educativos. Disponible en npm: @pmoo/testy.

➡️ English version here 👷 Guías para contribuir

Sponsors

10Pines logo

Para comenzar

npm install --save-dev @pmoo/testy (si utilizas npm)
yarn add --dev @pmoo/testy (si utilizas yarn)

Versiones de Node soportadas: 18.x o mayor (todas las versiones con soporte activo o de seguridad listadas aquí)

Uso

Escribiendo suites de test

Una suite de test no es más que un archivo cuyo nombre finaliza con _test.js y tiene la siguiente forma:

import { suite, test, assert } from '@pmoo/testy';

suite('una suite de tests aburrida', () => {
  test('42 es 42, no nos sorprende', () => {
    assert.that(42).isEqualTo(42);
  });
});

Una suite representa un agrupamiento de tests, y se define llamando a la función suite(name, body), que toma como parámetro el nombre de este agrupamiendo y una función sin argumentos, que representa el contenido de la suite.

Un test se escribe llamando a la función test(name, body), que toma como parámetro el nombre del caso de test y una función sin parámetros que representa el cuerpo del test.

Dentro del test se pueden evaluar diferentes aserciones que están documentadas más adelante.

Ejecutando Testy

Puedes ejecutar una suite de test con el siguiente comando:

$ npx testy my_test.js 

Or, al ejecutar testy sin argumentos se ejecutarán todos los tests, por defecto, que están dentro del directorio tests:

$ npx testy 

También se puede registrar testy como script de test script en package.json:

{
  ...
  "scripts": {
    "test": "npx testy"
  },
  ...
}

Para luego ejecutar los tests con npm test o yarn test.

Configurando Testy

Testy se puede configurar a través de un archivo llamado .testyrc.json que debe ser declarado en el directorio raíz del proyecto. Puedes usar la siguiente configuración como plantilla (los valores aquí mencionados son los valores por defecto):

{
  "directory": "./tests",   // directorio con los archivos de test
  "filter": ".*_test.js$",  // qué convención utilizar para el nombrado de archivos de test
  "language": "en",         // idioma de los mensajes de salida ("en" y "es" soportados por el momento)
  "failFast": false,        // habilita/deshabilita el modo "fail fast" (detener la ejecución en el primer fallo)
  "randomOrder": false      // habilita/deshabilita la ejecución de tests en orden aleatorio.
  "timeoutMs": 1000         // asigna el tiempo límite de ejecución por cada test (en milisegundos)
}

Estos son todos los parámetros de configuración que existen, ajústalos de acuerdo a tus necesidades. Siguiendo este ejemplo de configuración, lo que se va a ejecutar es cada suite de test dentro del directorio tests, cuyos nombres de archivos finalicen con *test.js.

Ejemplos y aserciones disponibles

  • Aserciones sobre valores booleanos:
    • assert.that(boolean).isTrue() o assert.isTrue(boolean). Realiza una comparación estricta contra true (object === true)
    • assert.that(boolean).isFalse() o assert.isFalse(boolean). Realiza una comparación estricta contra false (object === false)
  • Aserciones de igualdad de objetos:
    • assert.that(actual).isEqualTo(expected) o assert.areEqual(actual, expected).
    • assert.that(actual).isNotEqualTo(expected) o assert.areNotEqual(actual, expected)
    • Las aserciones de igualdad utilizan una comparación (deep) basada en el módulo assert de Node, y falla si los objetos que están siendo comparados tienen referencias cíclicas.
    • El criterio de igualdad en objetos no primitivos puede ser especificado:
      • Pasando una función adicional de comparación de dos parámetros a isEqualTo(expected, criteria) o areEqual(actual, expected, criteria)
      • Pasando un nombre de método que el objeto actual comprenda: isEqualTo(expected, 'myEqMessage') o areEqual(actual, expected, 'myEqMessage')
      • Por defecto, si actual entiende el mensaje equals, será utilizado para determinar la comparación
      • Si comparamos undefined con undefined usando isEqualTo(), el test fallará. Para chequear explícitamente por el valor undefined, se debe utilizar las aserciones isUndefined() o isNotUndefined() documentadas más adelante.
  • Aserciones de identidad de objetos:
    • assert.that(actual).isIdenticalTo(expected) o assert.areIdentical(actual, expected)
    • assert.that(actual).isNotIdenticalTo(expected) o assert.areNotIdentical(actual, expected)
    • Las aserciones de identidad comprueban si dos referencias apuntan al mismo objeto utilizando el operador ===.
  • Validar si un objeto es o no undefined:
    • assert.that(aValue).isUndefined() o assert.isUndefined(aValue)
    • assert.that(aValue).isNotUndefined() o assert.isNotUndefined(aValue)
  • Validar si un objeto es o no null:
    • assert.that(aValue).isNull() o assert.isNull(aValue)
    • assert.that(aValue).isNotNull() o assert.isNotNull(aValue)
  • Testeo de errores:
    • assert.that(() => { ... }).raises(error) o con una expresión regular .raises(/part of message/)
    • assert.that(() => { ... }).doesNotRaise(error)
    • assert.that(() => { ... }).doesNotRaiseAnyErrors()
  • Aserciones numéricas:
    • Comparación:
      • assert.that(aNumber).isGreaterThan(anotherNumber)
      • assert.that(aNumber).isLessThan(anotherNumber)
      • assert.that(aNumber).isGreaterThanOrEqualTo(anotherNumber)
      • assert.that(aNumber).isLessThanOrEqualTo(anotherNumber)
    • Redondeo
      • assert.that(aNumber).isNearTo(anotherNumber). Se puede pasar un segundo parámetro adicional que indica el número de dígitos de precisión que se van a considerar. Por defecto, son 4.
  • Aserciones sobre strings:
    • assert.that(string).matches(regexOrString) o assert.isMatching(string, regexOrString)
  • Inclusión de objetos en colecciones (Array y Set):
    • assert.that(collection).includes(object)
    • assert.that(collection).doesNotInclude(object)
    • assert.that(collection).includesExactly(...objects)
  • Verificar si una colección es o no vacía:
    • assert.that(collection).isEmpty() or assert.isEmpty(collection)
    • assert.that(collection).isNotEmpty() or assert.isNotEmpty(collection)
    • la colección a verificar puede ser un Array, un String o un Set

En la carpeta tests podrás encontrar más ejemplos y todas las posibles aserciones que puedes escribir. Testy está testeado en sí mismo.

Otras funcionalidades

  • Ejecutar código antes y después de cada test: al igual que muchas herramientas de testing, existe una forma de ejecutar código antes y después de cada test haciendo uso de before() y after() como parte de la definición de una suite. before() y after() reciben una función como parámetro y pueden utilizarse una sola vez por suite. Ejemplo:

    import { suite, test, assert, before, after } from '@pmoo/testy';
    
    suite('usando las funciones before() y after()', () => {
      let answer;
    
      before(() => {
        answer = 42;
      });
    
      test('la respuesta es 42', () => {
        assert.that(answer).isEqualTo(42);
      });
    
      after(() => {
        answer = undefined;
      });
    });
  • Soporte para tests pendientes: un test que no tenga cuerpo, será reportado como pendiente ([WIP]) y no se considerará una falla.

  • Soporte para tests asíncronos: si el código que estás testeando requiere de async, es posible hacer await dentro de la definicion del test y luego escribir las aserciones. También es posible hacer llamados asincrónicos en before() y after(). Ejemplo:

    import { suite, test, assert, before } from '@pmoo/testy';
    
    const promesaUno = async () => Promise.resolve(42);
    const promesaDos = async () => Promise.resolve(21);
    
    suite('usando async y await', () => {
      let respuestaUno;
    
      before(async () => {
        respuestaUno = await promesaUno();
      });
    
      test('comparando resultados de promesas', async () => {
        const respuestaDos = await promesaDos();
        assert.that(respuestaUno).isEqualTo(42);
        assert.that(respuestaDos).isEqualTo(21);
      });
    });
  • Modo "fail-fast": cuando está habilitado, se detiene apenas encuentra un test que falle o lance un error. Los tests restantes serán marcados como no ejecutados (skipped).

  • Ejecutar tests en orden aleatorio: una buena suite de tests no depende de un orden particular de tests para ejecutarse correctamentee. Activando esta configuración es una buena forma de asegurar eso.

  • Chequeo estricto de presencia de aserciones: si un test no evalúa ninguna aserción durante su ejecución, el resultado se considera un error. Básicamente, un test que no tiene aserciones es un "mal" test.

  • Explícitamente marcar un test como fallido o pendiente: Ejemplos:

    import { suite, test, fail, pending } from '@pmoo/testy';
    
    suite('marcando tests explícitamente como fallidos o pendientes', () => {
      test('marcando como fallido', () =>
        fail.with('no debería estar aquí'));
      
      test('marcando como pendiente', () =>
        pending.dueTo('no hubo tiempo de finalizarlo'));
    });

    Al ejecutar veremos los siguientes mensajes:

    [FAIL] marcando como fallido
      => no debería estar aquí
    [WIP] marcando como pendiente
      => no hubo tiempo de finalizarlo
    

¿Por qué?

¿Por qué tener una herramienta de tests cuando ya existen otras? La razón principal es que deseamos mantener la simplicidad, algo que no se puede encontrar en las principales herramientas de testing conocidas.

  • Cero dependencias: Este proyecto no depende de ningún otro paquete de npm para funcionar, lo que facilita su instalación, y lo hace más rápido: esencial para obtener feedback inmediato desarrollando con TDD. Esto es algo bueno también para instalar en lugares donde la conexión a internet no es buena y no queremos perder tiempo descargando múltiples dependencias.
  • Código orientado a objetos entendible: Esta herramienta es utilizada para enseñar, así que es muy común durante las clases mirar el código para entender cómo se ejecutan los tests, para entender lo que sucede. El objetivo es que los alumnos puedan comprender la herramienta e incluso realizar contribuciones a ella. Intentamos seguir buenas prácticas de diseño con objetos y de clean code en general.
  • Conjunto único de funcionalidad: Esta herramienta no sigue ninguna especificación ni trata de copiar la funcionalidad de enfoques conocidos de testing (como la forma "xUnit" la forma "xSpec"). La funcionalidad que existe, es la que tiene sentido que esté.

"Design Principles Behind Smalltalk" es una gran fuente de inspiración para este trabajo. Intentamos seguir los mismos principios aquí.

Para contribuir

Por favor revisar la guía para contribuciones.

Contribuyentes

Muchas gracias a estas maravillosas personas (emoji key):


Facundo Javier Gelatti

⚠️ 💻

Tomer Ben-Rachel

⚠️ 💻

Abraão Duarte

💻

adico

💻 ⚠️

Askar Imran

💻 ⚠️

Nigel Yong

💻

Chelsie Ng

💻

Pablo T

⚠️ 💻

Francisco Jaimes Freyre

💻 ⚠️ 📖

giovannipessiva

🌍

Abhishek Khatri

💻

Ignacio Robledo

💻 ⚠️

Marco Ellwanger

💻 ⚠️

María Belén Amat

💻 ⚠️ 🌍 📖

Este proyecto sigue la convención de all-contributors. Se aceptan contribuciones de todo tipo!