web-dev-qa-db-fra.com

Jasmine - Espionner un appel de méthode dans un constructeur

Je veux tester si la méthode suivante est appelée avec dans mon constructeur d'objet Javascript. D'après ce que j'ai vu dans la documentation de Jasmine, je peux espionner une méthode constructeur et je peux espionner des méthodes après qu'un objet a été instancié, mais je n'arrive pas à espionner une méthode avant que l'objet ne soit construit.

L'object:

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

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

Je veux faire quelque chose comme ça dans la spécification:

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

Espionnez directement la méthode du prototype:

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();
  });
});
104
Dave Newton

Globalement, je suis d'accord avec la réponse de Dave Newton ci-dessus. Cependant, il y a quelques cas Edge à cette approche que vous devriez considérer.

Prenez une variante de la solution de Dave, avec un autre cas de test:

// 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);
  });
});

Le deuxième test échouera car la configuration d'espionnage dans le premier test persiste à travers les limites du test dans la deuxième méthode; called_method n'incrémente pas call_count, donc this.call_count n'est pas égal à 1. Il est également possible de trouver des scénarios avec des faux positifs - des tests qui passent, qui ne devraient pas.

En plus de cela, parce que l'espion demeure, plus il y a d'instances Klass créées, plus le tas de mémoire que l'espion consommera est important, car l'espion enregistrera chaque appel à called_method. Ce n'est probablement pas un problème dans la plupart des circonstances, mais vous devez en être conscient, au cas où.

Une solution simple à ce problème serait de vous assurer que l'espion est supprimé après son utilisation. Cela peut sembler un peu moche, mais quelque chose comme ça fonctionne:

// 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;
  });

[NOTE - un petit avis pour terminer] Une meilleure solution serait de changer la façon dont vous écrivez le code de production pour rendre le code plus facile à tester. En règle générale, espionner les prototypes est probablement une odeur de code à éviter. Au lieu d'instancier des dépendances dans le constructeur, injectez-les. Au lieu de faire l'initialisation dans le constructeur, reportez-vous à une méthode d'initialisation appropriée.

11
alecmce