web-dev-qa-db-fra.com

Comment nettoyons-nous l'espionnage par programmation dans Jasmine?

Comment nettoyons-nous l'espion dans une suite de tests de jasmin par programme? Merci.

beforeEach(function() {
  spyOn($, "ajax").andCallFake(function(params){
  })
})

it("should do something", function() {
  //I want to override the spy on ajax here and do it a little differently
})
71
Tri Vuong

Je ne sais pas si c'est une bonne idée, mais vous pouvez simplement définir le drapeau isSpy sur la fonction sur false:

describe('test', function() {
    var a = {b: function() {
    }};
    beforeEach(function() {
        spyOn(a, 'b').andCallFake(function(params) {
            return 'spy1';
        })
    })
    it('should return spy1', function() {
        expect(a.b()).toEqual('spy1');
    })

    it('should return spy2', function() {
        a.b.isSpy = false;
        spyOn(a, 'b').andCallFake(function(params) {
            return 'spy2';
        })
        expect(a.b()).toEqual('spy2');
    })

})

Mais peut-être que c'est une meilleure idée de créer une nouvelle suite pour ce cas où vous avez besoin d'un autre comportement de votre espion.

5
Andreas Köberle

régler isSpy sur false est une très mauvaise idée, car vous espionnerez un espion et lorsque Jasmine effacera les espions à la fin de vos spécifications, vous n'obtiendrez pas la méthode originale. la méthode sera égale au premier espion.

si vous espionnez déjà une méthode et si vous souhaitez que la méthode d'origine soit appelée à la place, vous devez appeler andCallThrough(), qui remplacera le premier comportement d'espion.

par exemple

var spyObj = spyOn(obj,'methodName').andReturn(true);
spyObj.andCallThrough();

vous pouvez effacer tous les espions en appelant this.removeAllSpies() (this - spec)

118
Alissa

Je pense que c'est ce que . Reset () est pour:

spyOn($, 'ajax');

$.post('http://someUrl', someData);

expect($.ajax).toHaveBeenCalled();

$.ajax.calls.reset()

expect($.ajax).not.toHaveBeenCalled();
37
uglymunky

Ainsi, les espions sont réinitialisés automatiquement entre les spécifications.

En réalité, vous ne bénéficiez pas de la "restauration" de la fonction d'origine si vous utilisez andCallFake() dans un beforeEach() puis essayez de le modifier de force dans une spécification (ce qui explique probablement pourquoi vous empêcher de le faire).

Soyez donc prudent, surtout si votre espion est défini sur un objet global tel que jQuery.

Manifestation:

var a = {b:function() { return 'default'; } }; // global scope (i.e. jQuery)
var originalValue = a.b;

describe("SpyOn test", function(){
  it('should return spy1', function(){
    spyOn(a, 'b').andCallFake(function(params) {
      return 'spy1';
    })
    expect(a.b()).toEqual('spy1');
  });

  it('should return default because removeAllSpies() happens in teardown', function(){
    expect(a.b()).toEqual('default');
  });


  it('will change internal state by "forcing" a spy to be set twice, overwriting the originalValue', function(){
    expect(a.b()).toEqual('default');

    spyOn(a, 'b').andCallFake(function(params) {
      return 'spy2';
    })
    expect(a.b()).toEqual('spy2');

    // This forces the overwrite of the internal state
    a.b.isSpy = false;
    spyOn(a, 'b').andCallFake(function(params) {
      return 'spy3';
    })
    expect(a.b()).toEqual('spy3');

  });

  it('should return default but will not', function(){
    expect(a.b()).toEqual('default'); // FAIL

    // What's happening internally?
    expect(this.spies_.length).toBe(1);
    expect(this.spies_[0].originalValue).toBe(originalValue); // FAIL
  });

});

describe("SpyOn with beforeEach test", function(){
  beforeEach(function(){
    spyOn(a, 'b').andCallFake(function(params) {
      return 'spy1';
    })
  })

  it('should return spy1', function(){
    // inspect the internal tracking of spies:
    expect(this.spies_.length).toBe(1);
    expect(this.spies_[0].originalValue).toBe(originalValue);

    expect(a.b()).toEqual('spy1');
  });

  it('should return spy2 when forced', function(){
    // inspect the internal tracking of spies:
    expect(this.spies_.length).toBe(1);
    expect(this.spies_[0].originalValue).toBe(originalValue);

    // THIS EFFECTIVELY changes the "originalState" from what it was before the beforeEach to what it is now.
    a.b.isSpy = false;
    spyOn(a, 'b').andCallFake(function(params) {
        return 'spy2';
    })
    expect(a.b()).toEqual('spy2');
  });

  it('should again return spy1 - but we have overwritten the original state, and can never return to it', function(){
    // inspect the internal tracking of spies:
    expect(this.spies_.length).toBe(1);
    expect(this.spies_[0].originalValue).toBe(originalValue); // FAILS!

    expect(a.b()).toEqual('spy1');
  });
});

// If you were hoping jasmine would cleanup your mess even after the spec is completed...
console.log(a.b == originalValue) // FALSE as you've already altered the global object!
19
FilmJ

Dans Jasmine 2, l'état d'espionnage est conservé dans une instance de SpyStrategy. Vous pouvez vous procurer cette instance en appelant $.ajax.and. Voir le code source de Jasmin sur GitHub .

Donc, pour définir une méthode fictive différente, procédez comme suit:

$.ajax.and.callFake(function() { ... });

Pour rétablir la méthode d'origine, procédez comme suit:

$.ajax.and.callThrough();
9
titusd

Cela a fonctionné pour moi dans Jasmine 2.5 pour permettre la réinitialisation de Mock Ajax.

function spyOnAjax(mockResult) {
    // must set to true to allow multiple calls to spyOn:
    jasmine.getEnv().allowRespy(true);

    spyOn($, 'ajax').and.callFake(function () {
        var deferred = $.Deferred();
        deferred.resolve(mockResult);
        return deferred.promise();
    });
}

Ensuite, vous pouvez l'appeler plusieurs fois sans erreur. spyOnAjax (mock1); spyOnAjax (mock2);

5
user7054363

Ou tu peux le faire

describe('test', function() {
    var a, c;
    c = 'spy1';
    a = {
      b: function(){}
    };

    beforeEach(function() {
        spyOn(a, 'b').and.callFake(function () {
             return c;
        });
    })

    it('should return spy1', function() {
        expect(a.b()).toEqual('spy1');
    })

    it('should return spy2', function() {
        c = 'spy2';
        expect(a.b()).toEqual('spy2');
    })

})

Dans ce cas, vous utilisez le même Spy mais il vous suffit de changer la variable qui sera renvoyée.

1
RafaCianci

À partir de jasmine 2.5, vous pouvez utiliser ce paramètre global pour mettre à jour un espion dans vos scénarios de test:

jasmine.getEnv().allowRespy(true);
0
Hamzeen Hameem

Je poste cette réponse pour répondre au commentaire dans le code de l'OP @ Tri-Vuong - ce qui était la raison principale de ma visite sur cette page:

Je veux remplacer l'espion ... ici et le faire un peu différemment

Jusqu'à présent, aucune des réponses n'aborde ce point. Je vais donc publier ce que j'ai appris et résumer les autres réponses.

@Alissa l'a appelée correctement lorsqu'elle a expliqué pourquoi c'est une mauvaise idée de définir isSpy sur false - espionner efficacement un espion, ce qui entraînerait le comportement de démolition automatique de Jasmine qui ne fonctionnerait plus comme prévu. Sa solution (placée dans le contexte du PO et mise à jour pour Jasmine 2+) était la suivante:

beforeEach(() => {
  var spyObj = spyOn(obj,'methodName').and.callFake(function(params){
  }) // @Alissa's solution part a - store the spy in a variable
})

it("should do the declared spy behavior", () => {
  // Act and assert as desired
})

it("should do what it used to do", () => {
  spyObj.and.callThrough(); // @Alissa's solution part b - restore spy behavior to original function behavior
  // Act and assert as desired
})

it("should do something a little differently", () => {
  spyObj.and.returnValue('NewValue'); // added solution to change spy behavior
  // Act and assert as desired
})

Le dernier test it montre comment modifier le comportement d’un espion existant par un comportement autre que le comportement initial: "and- declare" le nouveau comportement du spyObj précédemment stocké dans la variable de la beforeEach(). Le premier test illustre mon cas d'utilisation. Je voulais qu'un espion se comporte d'une certaine manière pour la plupart des tests, mais le modifie ensuite pour quelques tests plus tard.

Pour les versions antérieures de Jasmine, modifiez les appels appropriés en .andCallFake(, .andCallThrough() et .andReturnValue( Respectivement.

0
LHM