Pruebas unitarias de AngularJS con Karma y Jasmine

⚡ Resumen inteligente

Las pruebas unitarias de AngularJS con Karma y Jasmine verifican controladores, directivas y servicios de forma aislada. Karma ejecuta las especificaciones en navegadores reales, Jasmine proporciona la sintaxis de aserciones y angular-mocks inyecta dependencias sin necesidad de un servidor en producción.

  • 🧱 División de herramientas: Karma es el ejecutor de pruebas, Jasmine es el marco de especificación y angular-mocks proporciona herramientas de ayuda para la inyección de dependencias.
  • ⚙️ Ruta de instalación: Instala karma, karma-chrome-launcher, karma-jasmine y karma-cli a través de npm, y luego genera karma.conf.js.
  • 📁 Regla de configuración: La matriz de archivos debe incluir angular.js, angular-mocks.js, los archivos fuente de la aplicación y la carpeta de pruebas en ese orden.
  • 🎛️ Prueba del controlador: Inyecta el servicio $controller, pasa un objeto $scope simulado y, a continuación, verifica los valores que asigna el controlador.
  • 🧩 Pruebas de directivas: Utilice $compile con $rootScope, llame a $digest e inspeccione el marcado renderizado que devuelve la directiva.
  • 🌐 Capa de extremo a extremo: ProtracTor controla una aplicación en ejecución y cuenta las filas repetidas producidas por ng-repeat.
  • 📌 Estado del soporte: AngularJS llegó al final de su ciclo de vida el 31 de diciembre de 2021, por lo que este flujo de trabajo debe considerarse como conocimiento de mantenimiento de sistemas heredados.

Pruebas unitarias de AngularJS con Karma y Jasmine

Una de las características más brillantes de AngularJS es la Pruebas aspecto. Cuando los desarrolladores en Google Al desarrollar AngularJS, tuvieron en cuenta las pruebas y se aseguraron de que todo el marco de trabajo de AngularJS fuera comprobable.

En AngularJS, las pruebas se realizan normalmente con Karma (un framework). Si bien es posible realizar pruebas de AngularJS sin Karma, este framework ofrece una funcionalidad tan excelente para probar código AngularJS que su uso resulta muy recomendable.

⚠️ Nota de versión: AngularJS (versión 1.x) llegó al final de su ciclo de vida oficial el 31 de diciembre de 2021, y ProtracTor se retiró en agosto de 2023. El flujo de trabajo descrito en esta página sigue siendo válido para el mantenimiento de aplicaciones AngularJS 1.x existentes. Los proyectos nuevos deben usar una versión moderna de Angular con un ejecutor compatible.

Introducción e instalación del marco Karma.

Karma es un herramienta de automatización de pruebas creado por el equipo de AngularJS en GoogleEl primer paso para usar Karma es instalarlo. Karma se instala mediante npm (un gestor de paquetes que facilita la instalación de módulos en una máquina local).

Instalación del Karma

La instalación de Karma a través de npm se realiza en un proceso de dos pasos.

Paso 1) Ejecute la siguiente línea desde la línea de comando

npm install karma karma-chrome-launcher karma-jasmine --save-dev

Donde,

  1. npm es la utilidad de línea de comandos del gestor de paquetes de Node.js que se utiliza para instalar módulos personalizados en cualquier máquina. Viene incluido Node.js.
  2. El parámetro de instalación indica a la utilidad de línea de comando npm que se requiere instalación.
  3. Hay 3 bibliotecas especificadas en la línea de comando que son necesarias para trabajar con karma.
    • karma es la biblioteca principal que se utilizará con fines de prueba.
    • karma-chrome-launcher es una biblioteca separada que permite que el navegador Chrome reconozca los comandos karma.
    • karma-jasmine: esto instala jasmine, que es un marco dependiente de Karma.

Paso 2) El siguiente paso es instalar la utilidad de línea de comandos karma. Esto es necesario para ejecutar comandos de línea de karma. La utilidad karma line se utilizará para inicializar el entorno de karma para realizar pruebas.

Para instalar la utilidad de línea de comando, ejecute la siguiente línea desde la línea de comando

npm install -g karma-cli
  • karma-cli se utiliza para instalar la interfaz de línea de comandos de karma, que se usará para escribir los comandos de karma en dicha interfaz. La opción -g la instala globalmente, de modo que el comando karma esté disponible desde cualquier carpeta.

Configuración del marco Karma.

El siguiente paso es configurar karma, lo cual se puede hacer mediante el comando

karma init

Después de ejecutar el paso anterior, karma creará un archivo karma.conf.js. El archivo probablemente se verá como el fragmento que se muestra a continuación.

files: [
  'node_modules/angular/angular.js',
  'node_modules/angular-mocks/angular-mocks.js',
  'lib/app.js',
  'tests/*.js'
]

Los archivos de configuración anteriores le indican al motor de ejecución de Karma lo siguiente

  1. angular.js – Esto le indica a Karma que tu aplicación depende de los módulos principales de AngularJS.
  2. angular-mocks.js – Esto le indica a Karma que utilice la funcionalidad de pruebas unitarias para AngularJS del archivo angular-mocks.js.
  3. Todos los archivos principales de la aplicación o de la lógica empresarial están presentes en la carpeta lib de su aplicación.
  4. La carpeta de pruebas contendrá todas las pruebas unitarias.

💡 Consejo: El orden importa dentro del array de archivos. angular.js debe cargarse antes que angular-mocks.js, y ambos deben cargarse antes que el código de tu aplicación; de lo contrario, el inyector no estará disponible cuando se ejecuten las especificaciones.

Para comprobar si karma está funcionando, cree un archivo llamado Sample.js, ingrese el siguiente código y colóquelo en el directorio de prueba.

describe('Sample test', function() {
  it('Condition is true', function() {
    expect('AngularJS').toBe('AngularJS');
  });
});

El código anterior tiene los siguientes aspectos

  1. La función de descripción se utiliza para dar una descripción de la prueba. En nuestro caso, le damos la descripción "Prueba de muestra" a nuestra prueba.
  2. La función 'it' se utiliza para darle un nombre a la prueba. En nuestro caso, le damos el nombre a nuestra prueba como "La condición es verdadera". El nombre de la prueba debe ser significativo.
  3. La combinación de las palabras clave "esperar" y "toBe" indica cuál es el valor esperado y real del resultado de la prueba. Si el valor real y el esperado son iguales, entonces la prueba pasará, de lo contrario fallará.

Cuando ejecute la siguiente línea en el símbolo del sistema, se ejecutará el archivo de prueba anterior

karma start

El siguiente resultado está tomado del IDE. Webstorm en el que se llevaron a cabo los pasos anteriores.

Configuración del marco Karma

  1. La salida viene en el explorador Karma dentro Webstorm. Esta ventana muestra la ejecución de todas las pruebas definidas en el marco de karma.
  2. Aquí puede ver que se muestra la descripción de la prueba ejecutada que es “Prueba de muestra”.
  3. A continuación, puede ver que se ejecuta la prueba en sí que tiene el nombre "La condición es verdadera".
  4. Tenga en cuenta que todas las pruebas tienen el ícono verde "Aceptar" al lado, lo que simboliza que todas las pruebas pasaron.

¿Qué es Jasmine en las pruebas unitarias de AngularJS?

Karma decide dónde se ejecutan las pruebas, mientras que Jasmine decide cómo se escriben. Jasmine es un marco de especificación basado en el comportamiento que proporciona el vocabulario utilizado en todos los ejemplos de esta página, por lo que el adaptador karma-jasmine se instala junto con el ejecutor.

Jazmín aporta cuatro elementos básicos:

  • describir(): Agrupa las especificaciones relacionadas en un conjunto y acepta una descripción legible de la unidad que se está probando.
  • él(): Declara una única especificación. El nombre debe indicar el comportamiento que se está verificando, en lugar del método que se está llamando.
  • expect() con comparadores: Compara un valor real con uno esperado. Los comparadores comunes incluyen toBe, toEqual, toContain y toBeDefined.
  • beforeEach() y afterEach(): Realiza la configuración inicial y final antes y después de cada especificación, lo que garantiza la independencia de cada prueba.

Jasmine también proporciona espías. Un espía reemplaza una función real con una grabación que la sustituye, de modo que se puede probar un controlador sin llamar a un endpoint HTTP real. Dado que Jasmine no necesita DOM ni ninguna biblioteca de aserciones propia, la combinación de Karma y Jasmine sigue siendo la forma más ligera de ejecutar AngularJS. JavaPruebas unitarias de script.

Prueba de controladores AngularJS

El marco de prueba de karma también tiene la funcionalidad de probar los controladores de un extremo a otro. Esto incluye pruebas del objeto $scope que se utiliza dentro de los Controladores.

Veamos un ejemplo de cómo podemos lograrlo.

En nuestro ejemplo,

Primero necesitaríamos definir un controlador. Este controlador llevaría a cabo los pasos mencionados a continuación.

  1. Cree una variable ID y asígnele el valor 5.
  2. Asigne la variable ID al objeto $scope.

Nuestra prueba comprobará la existencia de este controlador y también comprobará si la variable ID del objeto $scope está configurada en 5.

Primero debemos asegurarnos de que se cumpla el siguiente requisito previo

Instala la biblioteca angular-mocks a través de npm. Esto se puede hacer ejecutando la siguiente línea en la consola de comandos.

npm install angular-mocks --save-dev

Lo siguiente es modificar el archivo karma.conf.js para garantizar que se incluyan los archivos correctos para la prueba. El siguiente segmento solo muestra los archivos que forman parte de karma.conf.js que deben modificarse

files: ['lib/angular.js', 'lib/angular-mocks.js', 'lib/index.js', 'test/*.js']
  • El parámetro "archivos" básicamente le dice a Karma todos los archivos necesarios para la ejecución de las pruebas.
  • Los archivos angular.js y angular-mocks.js son necesarios para ejecutar las pruebas unitarias de AngularJS.
  • El archivo index.js contendrá nuestro código para el controlador
  • La carpeta de prueba contendrá todas nuestras pruebas de AngularJS.

A continuación se muestra nuestro código AngularJS, que se almacenará como un archivo Index.js en la carpeta de pruebas de nuestra aplicación.

El código siguiente simplemente hace lo siguiente:

  1. Crear una Módulo AngularJS llamada aplicación de muestra
  2. Crea un controlador llamado AngularJSController
  3. Crea una variable llamada ID, dale un valor de 5 y asígnala al objeto $scope
var sampleApp = angular.module('sampleApp', []);
sampleApp.controller('AngularJSController', function($scope) {
  $scope.ID = 5;
});

Una vez que el código anterior se ejecute correctamente, el siguiente paso sería crear un Caso de prueba para garantizar que el código se haya escrito y ejecutado correctamente.

El código para nuestra prueba será el que se muestra a continuación.

El código estará en un archivo independiente llamado ControllerTest.js, que se colocará en la carpeta de pruebas. El código que aparece a continuación solo realiza las siguientes tareas clave:

  1. Función beforeEach: esta función se utiliza para cargar nuestro módulo AngularJS llamado 'sampleApp' antes de la ejecución de la prueba. Tenga en cuenta que este es el nombre del módulo en un archivo index.js.
  2. El objeto $controller se crea como un objeto simulado para el controlador “AngularJSController”, que se define en nuestro archivo index.js. En cualquier prueba unitaria, un objeto simulado representa un objeto ficticio que se utilizará para la prueba. Este objeto simulado recreará el comportamiento de nuestro controlador.
  3. beforeEach(inject(function(_$controller_): esto se usa para inyectar el objeto simulado en nuestra prueba para que se comporte como el controlador real.
  4. var $ alcance = {}; Este es un objeto simulado que se está creando para el objeto $scope.
  5. var controller = $controller('AngularJSController', { $scope: $scope }); – Aquí estamos comprobando la existencia de un controlador llamado 'AngularJSController'. Aquí también estamos asignando todas las variables de nuestro objeto $scope en nuestro controlador en el archivo Index.js al objeto $scope en nuestro archivo de prueba.
  6. Finalmente, estamos comparando $scope.ID con 5
describe('AngularJSController', function() {
  beforeEach(module('sampleApp'));

  var $controller;

  beforeEach(inject(function(_$controller_){
    $controller = _$controller_;
  }));

  describe('$scope.ID', function() {
    it('Check the scope object', function() {
      var $scope = {};
      var controller = $controller('AngularJSController', { $scope: $scope });
      expect($scope.ID).toEqual(5);
    });
  });
});

La prueba anterior se ejecutará en el navegador karma y dará el mismo resultado que se mostró en el tema anterior.

Prueba de directivas AngularJS

El marco de pruebas Karma también tiene la funcionalidad para probar directivas personalizadas. Esto incluye la plantilla.URLque se utilizan dentro de directivas personalizadas.

Veamos un ejemplo de cómo podemos lograrlo.

En nuestro ejemplo, primero definiremos una directiva personalizada que hace lo siguiente

  1. Cree un módulo AngularJS llamado sampleApp
  2. Crea una directiva personalizada con el nombre guru99.
  3. Cree una función que devuelva una plantilla con una etiqueta de encabezado que muestre el texto "Esto es prueba de AngularJS".
var sampleApp = angular.module('sampleApp', []);
sampleApp.directive('guru99', function () {
  return {
    restrict: 'E',
    replace: true,
    template: '<h1>This is AngularJS Testing</h1>'
  };
});

Una vez que el código anterior se ejecute correctamente, el siguiente paso sería crear un caso de prueba para garantizar que el código se haya escrito y ejecutado correctamente. El código para nuestra prueba será el que se muestra a continuación.

El código estará en un archivo independiente llamado DirectiveTest.js, que se colocará en la carpeta de pruebas. El código que aparece a continuación solo realiza las siguientes tareas clave:

  1. Función beforeEach: esta función se utiliza para cargar nuestro módulo AngularJS llamado 'sampleApp' antes de la ejecución de la prueba.
  2. El servicio $compile se utiliza para compilar la directiva. Este servicio es obligatorio y debe declararse para que AngularJS pueda usarlo para compilar nuestra directiva personalizada.
  3. El `$rootScope` es el ámbito principal de cualquier aplicación AngularJS. Ya hemos visto el objeto `$scope` del controlador en capítulos anteriores. El objeto `$scope` es un objeto hijo del objeto `$rootScope`. Esto se declara aquí porque estamos modificando una etiqueta HTML en el DOM mediante nuestra directiva personalizada. Por lo tanto, necesitamos usar el servicio `$rootScope`, que detecta cualquier cambio dentro del documento HTML.
  4. var elemento = $compile(“ ”)($rootScope) – Esto se utiliza para comprobar si nuestra directiva se inyecta como debería. La directiva está registrada con el nombre en formato camel case guru99, por lo que AngularJS coincide con el elemento en minúsculas. en el marcado. Por lo tanto, esta instrucción se utiliza para realizar esa verificación.
  5. expect(element.html()).toContain(“This is AngularJS Testing”) – Esto se utiliza para indicar a la función expect que debe encontrar el elemento (en nuestro caso la etiqueta h1) que contiene el texto innerHTML “This is AngularJS Testing”.
describe('Unit testing directives', function() {
  var $compile,
      $rootScope;
  beforeEach(module('sampleApp'));

  beforeEach(inject(function(_$compile_, _$rootScope_){
    $compile = _$compile_;
    $rootScope = _$rootScope_;
  }));

  it('Check the directive', function() {
    // Compile a piece of HTML containing the directive
    var element = $compile("<guru99></guru99>")($rootScope);
    $rootScope.$digest();
    expect(element.html()).toContain("This is AngularJS Testing");
  });
});

La prueba anterior se ejecutará en el navegador karma y dará el mismo resultado que se mostró en el tema anterior.

Pruebas de extremo a extremo de aplicaciones AngularJS

El marco de pruebas de karma junto con un marco llamado ProtracTor tiene la funcionalidad de probar una aplicación web de principio a fin.

Por lo tanto, no se trata solo de probar directivas y controladores, sino también de cualquier otra cosa que pueda aparecer en una página HTML.

Veamos un ejemplo de cómo podemos lograrlo.

En nuestro ejemplo a continuación, tendremos una aplicación AngularJS que crea una tabla de datos usando la directiva ng-repeat.

  1. Primero, creamos una variable llamada "tutorial" y le asignamos algunos pares clave-valor en un solo paso. Cada par clave-valor se utilizará como datos al mostrar la tabla. Luego, la variable del tutorial se asigna al objeto de alcance para que se pueda acceder a él desde nuestra vista.
  2. Para cada fila de datos de la tabla, utilizamos la directiva ng-repeat. Esta directiva revisa cada par clave-valor en el objeto de alcance del tutorial utilizando la variable ptutor.
  3. Finalmente, estamos usando el etiqueta junto con los pares clave-valor (ptutor.Name y ptutor.Description) para mostrar los datos de la tabla.
<table>
  <tr ng-repeat="ptutor in tutorial">
    <td>{{ ptutor.Name }}</td>
    <td>{{ ptutor.Description }}</td>
  </tr>
</table>
</div>
<script type="text/javascript">
var app = angular.module('DemoApp', []);
app.controller('DemoController', function($scope) {
  $scope.tutorial = [
    {Name: "Controllers", Description: "Controllers in action"},
    {Name: "Models", Description: "Models and binding data"},
    {Name: "Directives", Description: "Flexibility of Directives"}
  ];
});

Una vez que el código anterior se ejecute correctamente, el siguiente paso sería crear un caso de prueba para garantizar que el código se haya escrito y ejecutado correctamente. El código para nuestra prueba será el que se muestra a continuación.

En realidad, nuestra prueba probará la directiva ng-repeat y garantizará que contenga 3 filas de datos como debería en el ejemplo anterior.

Primero debemos asegurarnos de que se cumpla el siguiente requisito previo

Instala el protracLibrería Tor a través de npm. Esto se puede hacer ejecutando la siguiente línea en la línea de comandos.

npm install -g protractor

El código para nuestra prueba será el que se muestra a continuación.

El código estará en un archivo independiente llamado CompleteTest.js, que se colocará en la carpeta de pruebas. El código que aparece a continuación solo realiza las siguientes tareas clave:

  1. La función del navegador la proporciona el protracLa biblioteca tor asume que nuestra aplicación AngularJS (con el código mostrado arriba) se está ejecutando en nuestro sitio. URL – http://localhost:8080/Guru99 /
  2. var list = element.all(by.repeater('ptutor in tutorial')); -Esta línea de código en realidad obtiene la directiva ng-repeat que se rellena con el código 'ptutor in tutorial'. El elemento y by.repeater son palabras clave especiales proporcionadas por el protracBiblioteca Tor que nos permite obtener detalles de la directiva ng-repeat.
  3. esperar(lista.count()).toEqual(3); – Por último, estamos usando la función expect para ver que, de hecho, estamos completando 3 elementos en nuestra tabla como resultado de la directiva ng-repeat.
describe('Unit testing end to end', function() {
  beforeEach(function() {
    browser.get('http://localhost:8080/Guru99/');
  });

  it('Check the ng directive', function() {
    var list = element.all(by.repeater('ptutor in tutorial'));
    expect(list.count()).toEqual(3);
  });
});

La prueba anterior se ejecutará en el navegador karma y dará el mismo resultado que se mostró en el tema anterior.

Karma contra Jazmín contra Protractor: Diferencias clave

Las tres herramientas que se utilizan en esta página suelen confundirse porque aparecen en la misma línea de comandos. Cada una ocupa una capa diferente de la pila de pruebas, como se muestra en la tabla a continuación.

Parámetro Karma Jazmín Protractor
Categoría corredor de pruebas Marco de especificaciones Marco de automatización de extremo a extremo
Trabajo principal Inicia navegadores e informa los resultados. Proporciona descripción, y espera Ejecuta una aplicación en vivo en un navegador real.
Nivel de prueba Unidad y de contacto Unidad e integración De extremo a extremo
Necesita un servidor en funcionamiento. No No Sí:
Archivo de configuración karma.conf.js Ninguno, corre dentro de un corredor PROtractor.conf.js
estado actual Obsoleto, solo para mantenimiento Mantenido activamente Se jubiló en agosto de 2023.

En resumen, Jasmine escribe la prueba, Karma la ejecuta y ProtracTor extiende la misma sintaxis de Jasmine a una página completamente renderizada.

Mejores prácticas para pruebas unitarias de AngularJS

Un pequeño conjunto de hábitos permite que una suite de AngularJS sea rápida y confiable a medida que la aplicación crece.

  1. Pruebe una unidad a la vez. Cargue únicamente el módulo que se está probando dentro de beforeEach y simule a cada colaborador para que un fallo apunte a un único archivo.
  2. Nunca llames a un servidor backend real. Utilice el servicio $httpBackend de angular-mocks para devolver respuestas predefinidas. De esta forma, las pruebas se ejecutan sin conexión y producen resultados idénticos en cualquier máquina.
  3. Mantenga los nombres de las especificaciones descriptivos del comportamiento. Un nombre como "devuelve 5 cuando el controlador se inicializa" se lee mejor en un informe que "test1".
  4. Siempre enjuague el ciclo digestivo. Llama a $rootScope.$digest() después de cambiar los datos del ámbito; de lo contrario, los enlaces y los observadores nunca se actualizarán y las aserciones fallarán por el motivo incorrecto.
  5. Mida la cobertura, pero evalúela con cuidado. Añade karma-coverage para ver las ramas sin probar. Una cobertura alta con aserciones débiles aún oculta defectos.
  6. Ejecuta el conjunto de pruebas en integración continua. Utiliza un lanzador de Chrome sin interfaz gráfica para que se ejecuten las mismas especificaciones en cada confirmación de cambios, en lugar de solo en la máquina del desarrollador.

La aplicación de estas reglas convierte los ejemplos anteriores de demostraciones aisladas en una red de seguridad de regresión para una base de código AngularJS heredada. Para obtener una visión más amplia, consulte la Tutorial de AngularJS .

Preguntas Frecuentes

El módulo que contiene ese proveedor no se cargó. Agréguelo dentro de beforeEach(module('yourModule')) y confirme que angular-mocks.js aparece en karma.conf.js después de angular.js; de lo contrario, el inyector no podrá resolver la dependencia.

Instala karma-chrome-launcher y configura la opción browsers a ChromeHeadless en karma.conf.js. De esta forma, el conjunto de herramientas se ejecutará dentro de pipelines de integración continua y contenedores donde no haya un servidor de visualización disponible.

Sí. Los asistentes de codificación de IA leen un controlador y elaboran especificaciones de Jasmine con ámbitos y comparadores simulados. RevExamine la salida con atención, ya que las aserciones generadas a menudo reiteran la implementación en lugar de verificar el comportamiento previsto.

Sí. Las herramientas de IA traducen las especificaciones de Jasmine a la sintaxis de Jest o Vitest e identifican las llamadas a angular-mocks que no tienen un equivalente directo. La verificación manual sigue siendo esencial, ya que los patrones de inyección de dependencias rara vez se convierten correctamente.

La mayoría de los equipos buscan una cobertura de sentencias del 70 al 80 por ciento en controladores y servicios. Intentar alcanzar una cifra mayor generalmente implica probar plantillas y bibliotecas de terceros, lo que aumenta los costos de mantenimiento sin detectar defectos reales.

Resumir este post con: