Header Ads Widget

Ticker

6/recent/ticker-posts

Tutorial de prueba unitaria de Jasmine Angular 9

 Jasmine es la biblioteca de JavaScript más popular para pruebas unitarias de aplicaciones web. Se usa de forma predeterminada como el marco de prueba predeterminado en proyectos de Angular generados con Angular CLI.

En este tutorial, diseñado para principiantes, lo ayudaremos a comenzar fácilmente con las pruebas unitarias usando Jasmine en Angular 9.

En esta primera parte de nuestro tutorial Jasmine Unit Testing en Angular 9, le presentaremos una guía rápida y completa para probar con Jasmine. Se le presentará a Jasmine, un popular marco de prueba basado en el comportamiento para JavaScript. También veremos un ejemplo práctico simple sobre cómo escribir pruebas unitarias con Jasmine que puede ayudarlo a verificar fácilmente si hay errores en su código.

En pocas palabras, veremos cómo escribir conjuntos de pruebas, especificaciones y expectativas y cómo aplicar comparadores Jasmine integrados o crear sus propios comparadores personalizados antes de aplicar estos conceptos en nuestro ejemplo de Angular 9.

También veremos cómo puede agrupar suites con el fin de organizar sus pruebas para bases de código más complejas.

Presentamos Jasmine para desarrolladores de Angular 9

Jasmine es un marco de desarrollo impulsado por el comportamiento de JavaScript muy popular (en BDD, se escriben pruebas antes de escribir el código real) para pruebas unitarias de aplicaciones JavaScript. Proporciona utilidades que se pueden utilizar para ejecutar pruebas automatizadas para código síncrono y asincrónico.

Jasmine tiene muchas características como:

  • Es rápido, tiene poca sobrecarga y no tiene dependencias externas.
  • Es una biblioteca que incluye baterías y ofrece todo lo que necesita para probar su código.
  • Está disponible tanto para Node como para el navegador.
  • Se puede usar con otros lenguajes como Python y Ruby.
  • No requiere DOM.
  • Proporciona una sintaxis limpia y fácil de entender y también una API enriquecida y sencilla.
  • Podemos usar lenguaje natural para describir las pruebas y los resultados esperados.

Jasmine es una herramienta de código abierto que está disponible bajo la licencia permisiva del MIT. En el momento de escribir estas líneas, la última versión principal es Jasmine 3.0, que proporciona nuevas funciones y algunos cambios importantes. La versión 2.99 de Jasmine proporcionará diferentes advertencias de obsolescencia para las suites que tienen un comportamiento diferente en la versión 3.0, lo que facilitará a los desarrolladores la migración a la nueva versión.

Jasmine no está vinculado a Angular, ya que puede usarlo con cualquier marco de JavaScript.

Puede leer sobre las nuevas funciones y los cambios importantes en este documento .

Usando Jasmine sin Angular 9

Puedes usar Jasmine de muchas formas diferentes:

  • a la antigua, al incluir el núcleo de Jasmine y los archivos de prueba con una <script>etiqueta,
  • como una herramienta CLI usando Node.js,
  • como una biblioteca en Node.js,
  • como parte de un sistema de compilación como Gulp.js o Grunt.js a través de grunt-contrib-jasmine y gulp-jasmine-browser

También puede usar Jasmine para probar su código Python con jasmine-py, que se puede instalar desde PyPI usando el pip install jasminecomando. Este paquete contiene un servidor web que sirve y ejecuta una suite Jasmine para su proyecto y un script CLI para ejecutar pruebas e integraciones continuas.

Jasmine también está disponible para proyectos Ruby a través de jasmine-gem, que puede instalarse agregando gem 'jasmine'a su Gemfile y ejecutándose bundle installIncluye un servidor para servir y ejecutar pruebas, un script CLI y también generadores para proyectos Ruby on Rails.

En los proyectos de Angular 9 generados con Angular CLI, no necesita configurar Jasmine en su proyecto, ya que se instala y configura automáticamente de forma predeterminada.

Ahora veamos primero cómo usar Jasmine con JavaScript.

Usar jazmín independiente

Comience descargando la última versión de Jasmine desde la página de lanzamientos .

Prueba de jazmín

Luego, simplemente extraiga el archivo zip, preferiblemente dentro de una carpeta en el proyecto que desea probar.

La carpeta contendrá un montón de archivos y carpetas predeterminados:

/src: contiene los archivos de origen que desea probar. Esto puede eliminarse si ya tiene la carpeta de su proyecto configurada o también puede usarse cuando sea apropiado para alojar su código fuente. /lib: contiene los archivos principales de Jasmine. /spec: contiene las pruebas que vas a escribir. SpecRunner.html: este archivo se utiliza como corredor de prueba. Ejecuta sus especificaciones simplemente ejecutando este archivo.

Este es el contenido de un SpecRunner.htmlarchivo predeterminado :

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Jasmine Spec Runner v3.2.1</title>

  <link rel="shortcut icon" type="image/png" href="lib/jasmine-3.2.1/jasmine_favicon.png">
  <link rel="stylesheet" href="lib/jasmine-3.2.1/jasmine.css">

  <script src="lib/jasmine-3.2.1/jasmine.js"></script>
  <script src="lib/jasmine-3.2.1/jasmine-html.js"></script>
  <script src="lib/jasmine-3.2.1/boot.js"></script>

  <!-- include source files here... -->
  <script src="src/Player.js"></script>
  <script src="src/Song.js"></script>

  <!-- include spec files here... -->
  <script src="spec/SpecHelper.js"></script>
  <script src="spec/PlayerSpec.js"></script>

</head>
<body>
</body>
</html>

Obviamente, debe cambiar los archivos incluidos de las carpetas /src/specpara que contengan su fuente real y los archivos de prueba.

Usando Jasmine como biblioteca

También puede utilizar Jasmine como biblioteca en su proyecto. Por ejemplo, el siguiente código importa y ejecuta Jasmine:

var Jasmine = require('jasmine');
var jasmine = new Jasmine();

jasmine.loadConfigFile('spec/support/jasmine.json');

jasmine.execute();

Primero requerimos / importamos Jasmine y usamos el loadConfigFile()método para cargar el archivo de configuración disponible desde la spec/support/jasmine.jsonruta y finalmente ejecutamos Jasmine.

Usando Jasmine a través de la CLI

También puede usar Jasmine desde la CLI, que le permite ejecutar fácilmente las pruebas de Jasmine y, de forma predeterminada, generar los resultados en la terminal.

Seguiremos este enfoque para ejecutar nuestras pruebas de ejemplo en esta guía, así que primero siga adelante y ejecute el siguiente comando para instalar Jasmine globalmente:

npm install -g jasmine

Es posible que deba ejecutar sudo para instalar paquetes npm globalmente, según su configuración de npm .

Ahora, cree una carpeta para su proyecto y navegue dentro de ella:

mkdir jasmine-project
cd jasmine-project

A continuación, ejecute el siguiente comando para inicializar su proyecto para Jasmine:

jasmine init

Este comando simplemente crea una carpeta de especificaciones y un archivo de configuración JSON. Esta es la salida del dircomando:

.
└── spec
    └── support
        └── jasmine.json

2 directories, 1 file

Este es el contenido de un jasmine.jsonarchivo predeterminado :

{
  "spec_dir": "spec",
  "spec_files": [
    "**/*[sS]pec.js"
  ],
  "helpers": [
    "helpers/**/*.js"
  ],
  "stopSpecOnExpectationFailure": false,
  "random": true
}
  • spec_dir: especifica dónde busca Jasmine los archivos de prueba.
  • spec_files: especifica los patrones de los archivos de prueba, por defecto todos los archivos JS que terminan con Spec o cadenas de especificaciones .
  • helpers: especifica dónde busca Jasmine los archivos de ayuda. Los archivos auxiliares se ejecutan antes que las especificaciones y se pueden usar para definir comparadores personalizados.
  • stopSpecOnExpectationFailure: cuando se establece en verdadero, se detendrá inmediatamente una especificación en la primera falla de una expectativa (se puede usar como una opción CLI a través de --stop-on-failure).
  • random: cuando se establece en verdadero, Jasmine ejecutará pseudoaleatoriamente los casos de prueba (se puede usar como una opción CLI a través de --random).

Las matrices spec_fileshelperstambién pueden contener patrones Glob (gracias al paquete node-glob ) para especificar rutas de archivo que son patrones que usualmente usa para especificar un conjunto de archivos cuando trabaja en Bash (por ejemplo ls *.js).

Si no usa la ubicación predeterminada para el jasmine.jsonarchivo de configuración, simplemente necesita especificar la ubicación personalizada a través de la jasmine --configopción.

Puede encontrar más opciones de CLI en los documentos oficiales .

Comprensión de Jasmine para desarrolladores de Angular 9

En esta sección aprenderemos sobre los elementos básicos de las pruebas de Jasmine, como suites, especificaciones, expectativas, comparadores y espías, etc. antes de que podamos verlos con un ejemplo en Angular 9.

En la carpeta de su proyecto, ejecute el siguiente comando para inicializar un nuevo módulo de nodo:

npm init -y

Esto creará un package.jsonarchivo con información predeterminada:

{
  "name": "jasmine-project",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

A continuación, cree un index.jsarchivo y agregue el siguiente código:

function fibonacci(n){

    if (n === 1) {
        return [0, 1];
    }
    else {
        var s = fibonacci(n - 1);
        s.push(s[s.length - 1] + s[s.length - 2]);
        return s;
    }
}
function isPrime(num){
    for (let i = 2; i < num; i++)
        if (num % i === 0) return false;
    return num !== 1 && num !== 0;
}
function isEven(n) {
    return n % 2 == 0;
}
function isOdd(n) {
    return Math.abs(n % 2) == 1;
}

function toLowerCase(str){
    return str.toLowerCase();
}
function toUpperCase(str){
    return str.toUpperCase();
}
function contains(str, substring, fromIndex){
    return str.indexOf(substring, fromIndex) !== -1;
}
function repeat(str, n){
    return (new Array(n + 1)).join(str);
}

module.exports = {
    fibonacci: fibonacci,
    isPrime: isPrime,
    isEven: isEven,
    isOdd: isOdd,
    toLowerCase: toLowerCase,
    toUpperCase: toUpperCase,   
    contains: contains,
    repeat: repeat
};

Suites

Una suite agrupa un conjunto de especificaciones o casos de prueba. Se usa para probar un comportamiento específico del código JavaScript que generalmente está encapsulado por un objeto / clase o una función. Se crea utilizando la función global Jasmine describe()que toma dos parámetros, el título del conjunto de pruebas y una función que implementa el código real del conjunto de pruebas.

Comencemos por crear nuestro primer conjunto de pruebas. Dentro de la speccarpeta, cree un MyJSUtilitiesSpec.jsarchivo y agregue:

describe("MyJSUtilities", function() {
 /* ... */
});

MyJSUtilities es el nombre de este conjunto de pruebas de nivel superior.

Cómo agrupar y suites Nest

Para organizar mejor y describir con precisión nuestro conjunto de pruebas, podemos anidar suites dentro de la suite de nivel superior. Por ejemplo, agreguemos dos suites a la suite MyJSUtilities :

describe("String Utils", function() {
 /*...*/
});

describe("Math Utils", function() {
 /*...*/
});

Dentro de la suite Math Utils , agreguemos también dos suites anidadas:

 describe("Basic Math Utils", function() {
   /* ... */
 });
 describe("Advanced Math Utils", function() {
   /* ... */
 }); 

Estamos agrupando pruebas relacionadas en pruebas para String Utils , Basic Math Utils y Advanced Math Utils y las anidamos dentro del conjunto de pruebas de nivel superior MyJSUtilities . Esto compondrá sus especificaciones como árboles similares a una estructura de carpetas.

La estructura de anidamiento se mostrará en el informe, lo que le facilitará la búsqueda de pruebas fallidas.

Cómo excluir suites

Puede desactivar temporalmente una suite utilizando la xdescribe()función. Tiene la misma firma (parámetros) que una describe()función, lo que significa que puede deshabilitar rápidamente sus suites existentes simplemente agregando un xa la función.

Las especificaciones dentro de una xdescribe()función se marcarán como pendientes y no se ejecutarán en el informe.

Especificaciones

Una especificación declara un caso de prueba que pertenece a un conjunto de pruebas. Esto se hace llamando a la función global Jasmine it()que toma dos parámetros, el título de la especificación (que describe la lógica que queremos probar) y una función que implementa el caso de prueba real.

Una especificación puede contener una o más expectativas. Cada expectativa es simplemente una afirmación que puede devolver truebien falsePara que se apruebe la especificación, todas las expectativas que pertenecen a la especificación deben ser, de lo truecontrario, la especificación falla.

Dentro de nuestra suite String Utils , agregue estas especificaciones:

describe("String Utils", function() {
  it("should be able to lower case a string",function() {
    /*...*/
  });
  it("should be able to upper case a string",function() {
    /*...*/
  });
  it("should be able to confirm if a string contains a substring",function() {
    /*...*/
  });
  it("should be able repeat a string multiple times",function() {
    /*...*/
  });

});

Dentro de nuestra suite Basic Math Utils , agreguemos algunas especificaciones:

describe("Basic Math Utils", function() {
  it("should be able to tell if a number is even",function() {
    /*...*/
  });   
  it("should be able to tell if a number is odd",function() {
    /*...*/
  });     
});

Para las utilidades matemáticas avanzadas , agreguemos las especificaciones:

describe("Advanced Math Utils", function() {
  it("should be able to tell if a number is prime",function() {
    /*...*/
  }); 
  it("should be able to calculate the fibonacci of a number",function() {
    /*...*/
  }); 
}); 

Cómo excluir especificaciones

Al igual que las suites, también puede excluir especificaciones individuales utilizando la xit()función que deshabilita temporalmente la it()especificación y marca la especificación como pendiente.

Expectativas

Las expectativas se crean utilizando la expect()función que toma un valor llamado real (esto puede ser valores, expresiones, variables, funciones u objetos, etc.). Las expectativas componen la especificación y se utilizan junto con las funciones de comparación (a través del encadenamiento) para definir lo que el desarrollador espera de una unidad específica de código para realizar.

Una función de comparador compara entre un valor real (pasado a la expect()función con la que está encadenada) y un valor esperado (pasado directamente como parámetro al comparador) y devuelve verdadero o falso que pasa o falla la especificación.

Puede encadenar la expect()función con varios comparadores. Para negar / invertir el resultado booleano de cualquier comparador, puede usar la notpalabra clave antes de llamar al comparador.

Implementemos las especificaciones de nuestro ejemplo. Por ahora usaremos que usaremos expect()con el nothing()comparador que es parte de los comparadores integrados que veremos un poco más adelante. Esto pasará todas las especificaciones ya que no esperamos nada en este momento.

describe("MyJSUtilities", function() {

describe(">String Utils", function() {
  it("should be able to lower case a string",function() {
    expect().nothing();
  });
  it("should be able to upper case a string",function() {
    expect().nothing();
  });
  it("should be able to confirm if a string contains a substring",function() {
    expect().nothing();
  });
  it("should be able repeat a string multiple times",function() {
    expect().nothing();
  });     

});

describe("Math Utils", function() {
 describe("Basic Math Utils", function() {
  it("should be able to tell if a number is even",function() {
    expect().nothing();
  });   
  it("should be able to tell if a number is odd",function() {
    expect().nothing();
  });   

 });
 describe("Advanced Math Utils", function() {
  it("should be able to tell if a number is prime",function() {
    expect().nothing();
  }); 
  it("should be able to calculate the fibonacci of a number",function() {
    expect().nothing();
  });    
 }); 
});

});

Esta es una captura de pantalla de los resultados en este punto:

Tutorial de prueba de jazmín

Tenemos ocho especificaciones aprobadas y cero fallas.

Puede utilizar comparadores integrados o también crear sus propios comparadores personalizados para sus necesidades específicas.

Matchers incorporados

Jasmine proporciona un rico conjunto de comparadores integrados. Veamos algunos de los importantes:

  • toBe() para probar la identidad,
  • toBeNull()para probar null,
  • toBeUndefined()/toBeDefined()para probar undefined/ no undefined,
  • toBeNaN() para la prueba de NaN (no es un número)
  • toEqual() para probar la igualdad,
  • toBeFalsy()/toBeTruthy() para probar la falsedad / veracidad, etc.

Puede encontrar la lista completa de comparadores en los documentos .

Implementemos ahora nuestras especificaciones con algunos de estos comparadores cuando sea apropiado. Primero importe las funciones que estamos probando en nuestro MyJSUtilitiesSpec.jsarchivo:

const utils = require("../index.js");

A continuación, comience con la suite String Utils y cambie expect().nothing()con las expectativas adecuadas.

Por ejemplo, para la primera especificación, esperamos que el toLowerCase()método se defina primero y, en segundo lugar, devuelva una cadena en minúsculas, es decir:

  it("should be able to lower case a string",function() {
        expect(utils.toLowerCase).toBeDefined();
        expect(utils.toLowerCase("HELLO WORLD")).toEqual("hello world");
  });

Este es el código completo de la suite:

describe(">String Utils", function() {
  it("should be able to lower case a string",function() {
    expect(utils.toLowerCase).toBeDefined();
    expect(utils.toLowerCase("HELLO WORLD")).toEqual("hello world");

  });

  it("should be able to upper case a string",function() {
    expect(utils.toUpperCase).toBeDefined();
    expect(utils.toUpperCase("hello world")).toEqual("HELLO WORLD");
  });

  it("should be able to confirm if a string contains a substring",function() {
    expect(utils.contains).toBeDefined();
    expect(utils.contains("hello world","hello",0)).toBeTruthy();

  });

  it("should be able repeat a string multiple times",function() {
    expect(utils.repeat).toBeDefined();
    expect(utils.repeat("hello", 3)).toEqual("hellohellohello");
  });     

});

Coincidentes personalizados

Jasmine proporciona la capacidad de escribir comparadores personalizados para implementar afirmaciones no cubiertas por los comparadores integrados o simplemente para hacer que las pruebas sean más descriptivas y legibles.

Por ejemplo, tomemos la siguiente especificación:

 it("should be able to tell if a number is even",function() {
    expect(utils.isEven).toBeDefined();
    expect(utils.isEven(2)).toBeTruthy();
    expect(utils.isEven(1)).toBeFalsy();
  });   

Supongamos que el isEven()método no está implementado. Si ejecutamos las pruebas obtendremos mensajes como la siguiente captura de pantalla:

Tutorial de prueba de jazmín

El mensaje de falla que recibimos dice Esperado indefinido para ser definido, lo que no nos da ni idea de lo que está sucediendo, hagamos que este mensaje sea más significativo en el contexto de nuestro dominio de código (esto será más útil para bases de código complejas). Para este asunto, creemos un comparador personalizado.

Creamos comparadores personalizados utilizando el addMatchers()método que toma un objeto compuesto por una o muchas propiedades que se agregarán como comparadores. Cada propiedad debe proporcionar una función de fábrica que toma dos parámetros util, que tiene un conjunto de funciones de utilidad para que las utilicen los comparadores (ver :) matchersUtil.jscustomEqualityTestersque debe pasarse si util.equalsse llama y debe devolver un objeto con una comparefunción que se llamará a comprobar la expectativa.

Necesitamos registrar el comparador personalizado antes de ejecutar cada especificación usando el beforeEach()método:

describe("/Basic Math Utils", function () {
beforeEach(function () {
jasmine.addMatchers({
hasEvenMethod:  function (util, customEqualityTesters) {
return {
compare:  function (actual, expected) {
var  result  = { pass:  utils.isEven  !==  undefined };
if (result.pass) {
result.message  =  "Expected isEven() to be not defined."
}
else {
result.message  =  "Expected isEven() to be defined."
}
return  result;
}
}
}
});
});
/*...*/
});

Luego podemos usar el comparador personalizado en lugar de expect(utils.isEven).toBeDefined():

expect().hasEvenMethod();

Esto nos dará un mejor mensaje de falla:

Prueba de jazmín

Usando beforeEach () y afterEach ()

Para inicializar y limpiar sus especificaciones, Jasmine proporciona dos funciones globales beforeEach()afterEach():

  • La beforeEachfunción se llama una vez antes de cada especificación en la suite donde se llama.
  • La afterEachfunción se llama una vez después de cada especificación en la suite donde se llama.

Por ejemplo, si necesita usar cualquier variable en su suite de prueba, simplemente puede declararla al inicio de la describe()función y poner cualquier código de inicialización o instanciación dentro de una beforeEach()función. Finalmente, puede usar la afterEach()función para restablecer las variables después de cada especificación, de modo que pueda tener pruebas unitarias puras sin la necesidad de repetir el código de inicialización y limpieza para cada especificación.

La beforeEach()función también se combina perfectamente con muchas API de Jasmine, como el addMatchers()método para crear comparadores personalizados o también con la done()función para esperar operaciones asincrónicas antes de continuar con las pruebas.

Reprobar una prueba

Puede forzar que una prueba falle utilizando el fail()método global disponible en Jasmine. Por ejemplo:

it("should explicitly fail", function () {
  fail('Forced to fail'); 
});

Debería obtener el siguiente error:

Tutorial de prueba de jazmín

Prueba de excepciones

Cuando está probando unitariamente su código, se pueden producir errores y excepciones, por lo que es posible que deba probar estos escenarios. Jasmine proporciona los comparadores toThrow()toThrowError()para probar cuando se lanza una excepción o para probar una excepción específica, respectivamente.

Por ejemplo, si tenemos una función que lanza una TypeErrorexcepción:

function throwsError() {
      throw new TypeError("A type error");
}

Podría escribir una especificación que probar si se lanza una excepción:

it('it should throw an exception', function () {
  expect(throwsError).toThrow();
});

O también puede usar la prueba para la TypeErrorexcepción específica :

it('it should throw a TypeError', function () {
  expect(throwsError).toThrowError(TypeError);
});

Entender a los espías

La mayoría de las veces, los métodos dependen de otros métodos, lo que significa que cuando está probando un método, también puede terminar probando sus dependencias. Esto no se recomienda en las pruebas, es decir, debe asegurarse de probar la función pura aislando el método y ver cómo se comporta dado un conjunto de entradas.

Jasmine proporciona espías que pueden usarse para espiar / escuchar llamadas a métodos en objetos e informar si se llama a un método y con qué contexto y argumentos.

Jasmine proporciona dos formas de espiar llamadas a métodos: utilizando spyOn()los createSpy()métodos .

Puede usarlo spyOn()cuando el método ya existe en el objeto; de lo contrario, debe usar el jasmine.createSpy()que devuelve una nueva función.

De forma predeterminada, un espía solo informará si se realizó una llamada sin llamar a través de la función espiada, es decir, la función dejará de ejecutarse, pero puede cambiar el comportamiento predeterminado utilizando estos métodos:

  • and.callThrough(): llamar a través de la función original,
  • and.returnValue(value): devuelve el valor especificado,
  • and.callFake(fn): llama a la función falsa en lugar de la original,
  • and.throwError(err): lanzar un error,
  • and.stub(): restablece el comportamiento de stubbing predeterminado.

Puede utilizar un espía para recopilar estadísticas en tiempo de ejecución sobre la función espiada, por ejemplo, si desea saber cuántas veces se llamó a su función.

Digamos que queremos asegurarnos de que nuestro toUpperCase()método esté utilizando el String.toUpperCase()método incorporado , simplemente necesitamos espiar String.toUpperCase()usando:

it("should be able to upper case a string", function () {
var  spytoUpperCase  =  spyOn(String.prototype, 'toUpperCase')
expect(utils.toUpperCase).toBeDefined();
expect(utils.toUpperCase("hello world")).toEqual("HELLO WORLD");
expect(String.prototype.toUpperCase).toHaveBeenCalled();
expect(spytoUpperCase.calls.count()).toEqual(1);
});

Ejemplo de prueba de jazmín

La prueba ha fallado debido a la segunda expectativa porque utils.toUpperCase("hello world")devolvió undefined en lugar del esperado HELLO WORLD, eso se debe a que, como mencionamos anteriormente, después de crear toUpperCase()el método espía no se ejecuta. Necesitamos cambiar este comportamiento predeterminado llamando a callThrough():

Tenga en cuenta que una spyfunción reemplaza la función espiada con un código auxiliar de forma predeterminada. Si necesita llamar a la función original en su lugar, puede agregar .and.callThrough()a su spyobjeto.

var  spytoUpperCase  =  spyOn(String.prototype, 'toUpperCase').and.callThrough();

Ahora todas las expectativas pasan.

También puede usar and.callFake()and.returnValue()falsificar la función espiada o simplemente el valor de retorno si no llama a través de la función real:

var  spytoUpperCase  =  spyOn(String.prototype, 'toUpperCase').and.returnValue("HELLO WORLD");
var  spytoUpperCase  =  spyOn(String.prototype, 'toUpperCase').and.callFake(function(){
return  "HELLO WORLD";
});

Ahora, si terminamos sin usar la versión incorporada String.toUpperCase()en nuestra propia utils.toUpperCase()implementación, obtendremos estas fallas:

Ejemplo de prueba de jazmín

Las dos expectativas expect(String.prototype.toUpperCase).toHaveBeenCalled() expect(spytoUpperCase.calls.count()).toEqual(1)han fracasado.

Cómo lidiar con la asincronicidad en Jasmine

Si el código que está probando contiene operaciones asincrónicas, necesita una forma de informarle a Jasmine cuándo se completaron las operaciones asincrónicas.

De forma predeterminada, Jasmine espera asynca que finalice cualquier operación asincrónica, definida por una devolución de llamada, una promesa o la palabra clave. Si encuentra una devolución de llamada Jasmine, promesa o asíncrono palabra clave en una de estas funciones: beforeEachafterEachbeforeAllafterAll, y itse esperará a que el asíncrona por hacer antes de pasar a la siguiente operación.

Utilizando done()con beforeEach()it()..

https://volaresystems.com/blog/post/2014/12/09/Testing-async-calls-with-Jasmine

Tomemos nuestro ejemplo simulateAsyncOp()que simula una operación asincrónica usando setTimeout()En un escenario del mundo real, esto puede ser una solicitud Ajax o cualquier cosa similar que suceda de forma asincrónica:

function  simulateAsyncOp(callback){
setTimeout(function () {
callback();
}, 2000);
}

Para probar esta función, podemos usar la beforeEach()función con la done()devolución de llamada especial Nuestro código debe invocar done()para decirle a Jasmine que la operación asincrónica se ha completado:

describe("/Async Op", function () {

var  asyncOpCompleted  =  false;

beforeEach(function (done) {
utils.simulateAsyncOp(function(){
  asyncOpCompleted  =  true;
  done();
});
});
it("should be able to tell if the async call has completed", function () {
  expect(asyncOpCompleted).toEqual(true);
});
});

Podemos notar rápidamente un inconveniente de este método, necesitamos escribir nuestro código para aceptar la done()devolución de llamada. En nuestro caso, no codificamos el done()método en nuestro, simulateAsyncOp(fn)pero proporcionamos un parámetro de devolución de llamada solo para poder llamar done().

Usar promesas

Si no desea crear un código que dependa de cómo escriba su prueba, puede usar una promesa en su lugar y llamar a la done()devolución de llamada cuando la promesa se haya resuelto o mejor aún, en Jasmine 2.7+, si su código devuelve un Promise, Jasmine lo hará espere hasta que se resuelva o rechace antes de ejecutar el siguiente código.

Usando async / await

Jasmine 2.7+ admite asyncawaitsolicita especificaciones. Esto le libera de poner afirmaciones en bloques .then().catch().

it("should work with async/await", async () => {
let  completed  =  false;
completed  =  await  utils.simulateAsyncOp();
expect(completed).toEqual(true);
});

Esta es la implementación de simulateAsyncOp:

function  simulateAsyncOp() {
  return  new  Promise(resolve  => {
    setTimeout(() => {
      resolve(true);
    }, 1000);
  });
}

Usando Jasmine Clock

El reloj Jasmine se utiliza para probar código asincrónico que depende de funciones de tiempo, como setTimeout()de la misma manera que probamos el código sincrónico simulando API basadas en el tiempo con métodos personalizados. De esta manera, puede ejecutar las funciones probadas de forma síncrona controlando o haciendo avanzar manualmente el reloj.

Puede instalar el reloj Jasmine llamando a la jasmine.clock().installfunción en su especificación o suite.

Después de usar el reloj, debe desinstalarlo para restaurar las funciones originales.

Con Jasmine clock, puede controlar JavaScript setTimeouto las setIntervalfunciones haciendo tictac en el reloj para avanzar en el tiempo usando la jasmine.clock().tickfunción, que toma la cantidad de milisegundos con los que puede moverse.

También puede usar el Jasmine Clock para simular la fecha actual.

beforeEach(function () {
jasmine.clock().install();
});

afterEach(function() {
jasmine.clock().uninstall();
});

it("should call the asynchronous operation synchronously", function() {
var  completed  =  false;
utils.simulateAsyncOp(function(){
completed  =  true;
});
expect(completed).toEqual(false);
jasmine.clock().tick(1001);
expect(completed).toEqual(true);
});

Esta es la simulateAsyncOpfunción:

function  simulateAsyncOp(callback){
setTimeout(function () {
callback();
}, 1000);
}

En caso de que no haya especificado una hora para la mockDatefunción, utilizará la fecha actual.

Manejo de errores

Si su código asincrónico falla debido a algún error, desea que sus especificaciones fallen correctamente. A partir de Jasmine 2.6+, cualquier error no controlado se envía a la especificación actualmente ejecutada.

Jasmine también proporciona una forma que puede usar si necesita fallar explícitamente sus especificaciones:

  • usando la done()devolución de llamada con beforeEach()llamando al done.fail(err)método,
  • simplemente pasando un error a la done(err)devolución de llamada (Jasmine 3+),
  • llamando al reject()método de a Promise.

Conclusión

En esta guía, presentamos Jasmine para desarrolladores de Angular 9 y vimos cómo comenzar a usar Jasmine para realizar pruebas unitarias de su código JavaScript.

Publicar un comentario

0 Comentarios