Un moyen de modifier les espions Jasmine en fonction d'arguments?

147

J'ai une fonction que j'aimerais tester qui appelle deux fois une méthode d'API externe, en utilisant différents paramètres. Je voudrais moquer cette API externe avec un espion Jasmine et renvoyer différentes choses en fonction des paramètres. Y a-t-il un moyen de faire cela dans Jasmine? Le mieux que je puisse trouver est un hack utilisant andCallFake:

var functionToTest = function() {
  var userName = externalApi.get('abc');
  var userId = externalApi.get('123');
};


describe('my fn', function() {
  it('gets user name and ID', function() {
    spyOn(externalApi, 'get').andCallFake(function(myParam) {
      if (myParam == 'abc') {
        return 'Jane';
      } else if (myParam == '123') {
        return 98765;
      }
    });
  });
});
Jmr
la source

Réponses:

215

Dans les versions 3.0 et supérieures de Jasmine, vous pouvez utiliser withArgs

describe('my fn', function() {
  it('gets user name and ID', function() {
    spyOn(externalApi, 'get')
      .withArgs('abc').and.returnValue('Jane')
      .withArgs('123').and.returnValue(98765);
  });
});

Pour les versions de Jasmine antérieures à 3.0, callFakec'est la bonne voie à suivre, mais vous pouvez la simplifier en utilisant un objet pour contenir les valeurs de retour

describe('my fn', function() {
  var params = {
    'abc': 'Jane', 
    '123': 98765
  }

  it('gets user name and ID', function() {
    spyOn(externalApi, 'get').and.callFake(function(myParam) {
     return params[myParam]
    });
  });
});

Selon la version de Jasmine, la syntaxe est légèrement différente:

  • 1.3.1: .andCallFake(fn)
  • 2.0: .and.callFake(fn)

Ressources:

Andreas Köberle
la source
11
C'est maintenant and.callFake- jasmine.github.io/2.2 / ... >
Lucy Bain
J'ai dû renvoyer des promesses différentes, donc le retour était légèrement différent: return q.when (params [myParam]) ;. Sinon, c'était une solution sur place à mon problème. Ma solution de rêve serait de changer les appels "and.returnValue".
Bill Turner
7
sent que le jasmin devrait avoir une meilleure façon de le déclarer. Comme spyOn(fake, 'method').withArgs('abc').and.returnValue('Jane')et spyOn(fake, 'method').withArgs('123').and.returnValue(98765).
jrharshath
@jrharshath .withArgsne fonctionne pas pour moi dans Jasmine 2.0
hemkaran_raghav
1
.withArgsn'est pas vraiment disponible - je voulais dire qu'une telle méthode aurait du sens lors de l'écriture de tests.
jrharshath
9

Vous pouvez également utiliser $providepour créer un espion. Et simulez l'utilisation and.returnValuesau lieu de and.returnValuepasser des données paramétrées.

Selon les documents Jasmine: En enchaînant l'espion avec and.returnValues, tous les appels à la fonction renverront des valeurs spécifiques dans l'ordre jusqu'à ce qu'il atteigne la fin de la liste des valeurs de retour, auquel cas il retournera undefined pour tous les appels suivants.

describe('my fn', () => {
    beforeEach(module($provide => {
        $provide.value('externalApi', jasmine.createSpyObj('externalApi', ['get']));
    }));

        it('get userName and Id', inject((externalApi) => {
            // Given
            externalApi.get.and.returnValues('abc','123');

            // When
            //insert your condition

            // Then
            // insert the expectation                
        }));
});
Akhouri
la source
C'est la bonne réponse, car un test doit toujours savoir exactement comment un espion sera appelé, et doit donc simplement l'utiliser returnValuespour prendre en charge plusieurs appels
Schmuli
2
Juste pour clarifier la réponse d'Akhouri: cette méthode ne fonctionne que lorsque le externalApi.get.and.returnValues('abc','123')est appelé dans la itfonction. Sinon, si vous définissez une liste de valeurs, ailleurs, cela ne fonctionnera jamais car l'ordre dans lequel les tests sont exécutés n'est pas prévisible. En fait, les tests ne devraient pas dépendre de l'ordre dans lequel ils sont exécutés.
avi.elkharrat
0

Dans mon cas, j'avais un composant que je testais et, dans son constructeur, il y a un service de configuration avec une méthode appelée getAppConfigValue qui est appelée deux fois, à chaque fois avec des arguments différents:

constructor(private configSvc: ConfigService) {
  this.configSvc.getAppConfigValue('a_string');
  this.configSvc.getAppConfigValue('another_string');
}

Dans mes spécifications, j'ai fourni le ConfigService dans le TestBed comme suit:

{
  provide: ConfigService,
  useValue: {
    getAppConfigValue: (key: any): any {
      if (key === 'a_string) {
        return 'a_value';
      } else if (key === 'another_string') {
        return 'another_value';
      }
    }
  } as ConfigService
}

Ainsi, tant que la signature de getAppConfigValue est la même que celle spécifiée dans le ConfigService réel, ce que la fonction fait en interne peut être modifié.

Guillermo
la source