Cómo burlarse de la clase ES6

Esta publicación tiene la intención de mostrar cómo burlarse de una clase en JavaScript con el propósito de pruebas unitarias. La publicación está dirigida a desarrolladores que vienen a ES6 desde entornos que tienen características como inyección de dependencia e interfaces. La mayor parte de la publicación cubrirá qué es una clase en ES6 y por qué los necesitamos. Una vez que se cubra el modelo subyacente, la burla del mismo será obvia. Esta publicación lo llevará a través del viaje de crear objetos en JavaScript.

Objeto literal

Object es un primer ciudadano en JavaScript. La forma increíblemente fácil de crear un objeto hace que este lenguaje sea simple de comenzar. Aquí hay un objeto de muestra.

var persona = {
  nombre: 'Velu'
};

Es así de simple. Para crear un objeto, comienza con un objeto (y no una clase). En ese sentido, diría que JavaScript es verdadero Orientado a objetos (y lenguajes como Java están orientados a clases, ya que comenzará a clasificar para crear objetos).

Extendamos el ejemplo anterior para agregar un comportamiento a nuestro objeto de muestra, sayName.

var person1 = {
  nombre: 'Velu',
  sayName: function () {
    console.log (this.name);
  }
}

Con este objeto, si necesita crear varios objetos, debe duplicar la función sayName en cada uno de los objetos. Esto significaría:

var person1 = {
  nombre: 'Velu',
  sayName: function () {
    console.log (this.name);
  }
}
var person2 = {
  nombre: 'Raj',
  sayName: function () {
    console.log (this.name);
  }
}

Si terminas creando muchas personas, esto significaría que hay mucha duplicación de código. Para superar esto viene la función Constructor.

Función constructora

Escribamos una función de constructor de muestra y hablemos al respecto:

función Persona (nombre) {
  this.name = nombre;
  this.sayName = function () {
    console.log (this.name);
  }
}

A primera vista, esta es otra función más en JavaScript. Lo que hace que la función del constructor sea especial no es la función en sí, sino cuando está asociada con la nueva palabra clave. Entonces, una función de constructor se puede usar como en:

var person1 = nueva persona ('Velu');

La línea anterior creará un objeto y, literalmente, el código se transformará en:

var person1 = {
  nombre: 'Velu',
  sayName: function () {
    console.log (this.name);
  }
}

Ahora para crear más personas no hay duplicación de código. El plano se define en la función Constructor y JavaScript lo convierte mágicamente en un objeto en tiempo de ejecución.

Con esto, no hay duplicación de código, pero el objeto subyacente todavía tiene una pieza duplicada. La función sayName se define solo una vez en la función constructora, pero aún se copia en cada objeto que se crea. Esto significa más memoria. Por ejemplo, si creo mil millones de personas en mi Mac Book Pro, el proceso de nodo se bloquea sin memoria. Eso trae la necesidad de compartir el modelo de objetos no solo en tiempo de código sino también en tiempo de ejecución.

Prototipo

Entonces, lo que necesitamos es un mecanismo para compartir funciones entre los objetos. En JavaScript, el mecanismo para compartir código nuevamente es otro objeto llamado prototipo. Cada objeto de JavaScript que se crea tiene un enlace incorporado llamado prototipo a otro objeto. Este objeto es compartido por todos los objetos creados usando la misma función de constructor. El diseño a continuación muestra esto:

Ahora, el paso obvio es mover la función sayName del objeto a su prototipo. El siguiente código hace esto:

función Persona (nombre) {
  this.name = nombre;
}
Person.prototype.sayName = function () {
    console.log (this.name);
}

También JavaScript garantiza las siguientes 2 cosas para que todo sea posible:

  1. Si se accede a una propiedad en un objeto y no está presente en ese objeto, se buscará y se accederá en su prototipo
  2. La referencia "this" en las funciones adjuntas al prototipo con un enlace mágico al objeto sobre el que se invoca

Por lo tanto, la siguiente invocación de sayName garantiza que este apuntará al objeto person1 durante el tiempo de ejecución.

var person1 = nueva persona ('Velu');
person1.sayName ();

Clase ES6

Ahora, llegando a la clase ES6, finalmente, es solo un azúcar sintáctico que generará la función Constructor durante el tiempo de ejecución. La sintaxis de clase solo trae esto de manera más concisa:

Persona de clase {
  constructor (nombre) {
    this.name = nombre;
  }
  sayName () {
    console.log (this.name);
  }
}

Habiendo entendido qué clase de ES6 está bajo el capó, aún hay otro concepto de JavaScript para entender. Considere los siguientes módulos de JavaScript y sus dependencias:

En esta estructura, el Módulo A importa tanto B como C. Ahora, cuando el Módulo B también importa C, el módulo cargado por A se reutiliza y no es uno nuevo. Esto significa que si una clase ES6 se exporta desde el Módulo C, la definición de la misma se puede alterar en A y la definición alterada se reflejará en C. Armado con esta comprensión sobre las clases ES6 y el sistema de módulos, finalmente veamos cómo burlarse.

Burlándose de la clase ES6

Consideremos el siguiente escenario de prueba. El empleado depende de un auxiliar de clase de utilidad para obtener un número aleatorio. El módulo de Empleado se vería así:

const Helper = require ('./ helper');

Empleado de clase {
  constructor (nombre) {
    this.name = nombre;
  }

  getId () {
    const helper = nuevo Helper ();
    const id = helper.getRandom ();

    devuelve `$ {this.name} - $ {id}`;
  }
}

module.exports = Empleado;

La prueba unitaria de Employee tiene que burlarse del método getRandom para conducir la prueba en Employee.getId. El flujo se ve así:

Ahora con todos los detalles que hemos reunido hasta ahora, el código de burla es obvio y dice así:

const expect = require ('chai'). expect;
const sinon = require ('sinon');

const Empleado = require ('../ src / employee');
const Helper = require ('../ src / helper');

describe ('prueba de empleado', () => {
  it ('debería devolver la identificación correcta', () => {
    // Organizar
    sinon.stub (Helper.prototype, 'getRandom'). callsFake (() => 1);

    // Ley
    const employee = nuevo empleado ('Velu');
    const id = employee.getId ();

    // Afirmar
    esperar (id) .to.equals ('Velu-1');
  });
});

Básicamente, para burlarse de un método en la clase Helper, solo obtenga la referencia de la función a través del prototipo de clase y aplíquelo. Esta línea bloquea la función getRandom para que siempre devuelva 1 para que la operación Employee.getId pueda validarse.

sinon.stub (Helper.prototype, 'getRandom'). callsFake (() => 1);

La base del código para esto se puede encontrar https://github.com/madhanganesh/es6-class-mocking-sample