Jasmine-Espiando una llamada a un método dentro de un constructor


Quiero probar si el siguiente método se llama con en mi constructor de objetos Javascript. Por lo que he visto en la documentación de Jasmine, puedo espiar un método constructor y puedo espiar métodos después de que se ha instanciado un objeto, pero parece que no puedo espiar un método antes de que se construya el objeto.

El objeto:

Klass = function() {
    this.called_method();
};

Klass.prototype.called_method = function() {
  //method to be called in the constructor.
}

Quiero hacer algo como esto en la especificación:

it('should spy on a method call within the constructor', function() {
    spyOn(window, 'Klass');
    var obj = new Klass();
    expect(window.Klass.called_method).toHaveBeenCalled();
});
Author: Levi McCallum, 2012-01-05

2 answers

Espiar directamente en el método prototipo:

describe("The Klass constructor", function() {
  it("should call its prototype's called_method", function() {
      spyOn(Klass.prototype, 'called_method');  //.andCallThrough();
      var k = new Klass();
      expect(Klass.prototype.called_method).toHaveBeenCalled();
  });
});
 100
Author: Dave Newton,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-01-05 00:20:40

En términos generales, estoy de acuerdo con la respuesta anterior de Dave Newton. Sin embargo, hay algunos casos extremos de este enfoque que debe considerar.

Tome una variación de la solución de Dave, con otro caso de prueba:

// production code
var Klass = function() {
  this.call_count = 0;
  this.called_method();
};
Klass.prototype.called_method = function() {
  ++this.call_count;
};

// test code
describe("The Klass constructor", function() {
  it("should call its prototype's called_method", function() {
    spyOn(Klass.prototype, 'called_method');
    var k = new Klass();
    expect(k.called_method).toHaveBeenCalled();
  });
  it('some other test', function() {
    var k = new Klass();
    expect(k.call_count).toEqual(1);
  });
});

La segunda prueba fallará porque la configuración del espía en la primera prueba persiste a través de los límites de la prueba en el segundo método; called_method no incrementa call_count, así que esto.call_count no es igual a 1. También es posible crear escenarios con falsos positivos - pruebas que pasan, eso no debería.

Además de esto, debido a que el espía permanece, cuantas más instancias Klass se creen, mayor será el montón de memoria que consumirá el espía, porque el espía grabará cada llamada a called_method. Esto probablemente no es un problema en la mayoría de las circunstancias, pero usted debe ser consciente de ello, por si acaso.

Una solución simple a este problema sería asegurarse de que el espía se elimina después de que se ha utilizado. Puede parecer un poco feo, pero algo como esto obras:

// test code
describe("The Klass constructor", function() {
  it("should call its prototype's called_method", function() {
    var spy = jasmine.createSpy('called_method');
    var method = Klass.prototype.called_method;
    Klass.prototype.called_method = spy;
    var k = new Klass();
    expect(spy).toHaveBeenCalled();
    Klass.prototype.called_method = method;
  });

[NOTA - un poco de opinión para terminar] Una mejor solución sería cambiar la forma de escribir código de producción para que el código sea más fácil de probar. Como regla general, espiar prototipos es probablemente un olor a código que debe evitarse. En lugar de crear instancias de dependencias en el constructor, que se inyectan. En lugar de hacer inicialización en el constructor, defer to an appropriate init method.

 11
Author: alecmce,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-13 18:42:57