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
})
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.
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)
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();
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!
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();
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);
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.
À 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);
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.