¿Alguna forma de modificar los espías de Jasmine en función de los argumentos?

147

Tengo una función que me gustaría probar que llama a un método API externo dos veces, usando diferentes parámetros. Me gustaría burlarme de esta API externa con un espía Jasmine, y devolver diferentes cosas según los parámetros. ¿Hay alguna manera de hacer esto en Jasmine? Lo mejor que puedo encontrar es un hack usando 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
fuente

Respuestas:

215

En Jasmine versiones 3.0 y superiores puedes usar 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);
  });
});

Para las versiones de Jasmine anteriores a la 3.0 callFakees el camino correcto, pero puede simplificarlo usando un objeto para contener los valores de retorno

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

Dependiendo de la versión de Jasmine, la sintaxis es ligeramente diferente:

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

Recursos:

Andreas Köberle
fuente
11
Esto es ahora and.callFake- jasmine.github.io/2.2/… >
Lucy Bain
Tuve que devolver diferentes promesas, por lo que el retorno se veía ligeramente diferente: return q.when (params [myParam]) ;. De lo contrario, este fue un punto en la solución a mi problema. La solución de mis sueños sería cambiar las llamadas "and.returnValue".
Bill Turner
77
siente que el jazmín debería tener una mejor manera de declarar esto. Me gusta spyOn(fake, 'method').withArgs('abc').and.returnValue('Jane')y spyOn(fake, 'method').withArgs('123').and.returnValue(98765).
jrharshath
@jrharshath .withArgsno funciona para mí en jazmín 2.0
hemkaran_raghav 05 de
1
.withArgsno está realmente disponible; quise decir que tal método tendría sentido al escribir pruebas.
jrharshath
9

También puedes usar $providepara crear un espía. Y simula usar usando en and.returnValueslugar de and.returnValuepasar datos parametrizados.

Según los documentos de Jasmine: al encadenar al espía and.returnValues, todas las llamadas a la función devolverán valores específicos en orden hasta que llegue al final de la lista de valores de retorno, momento en el que volverá indefinido para todas las llamadas posteriores.

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
fuente
Esta es la respuesta correcta, ya que una prueba siempre debe saber exactamente cómo se llamará a un espía y, por lo tanto, solo debe usarse returnValuespara admitir varias llamadas
Schmuli
2
Solo para aclarar la respuesta de akhuri: este método solo funciona cuando externalApi.get.and.returnValues('abc','123')se llama dentro de la itfunción. De lo contrario, si establece una lista de valores, de lo contrario, nunca funcionará porque el orden en que se ejecutan las pruebas no es predecible. De hecho, las pruebas no deberían depender del orden en que se ejecutan.
avi.elkharrat
0

En mi caso, tenía un componente que estaba probando y, en su constructor, hay un servicio de configuración con un método llamado getAppConfigValue que se llama dos veces, cada vez con diferentes argumentos:

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

En mi especificación, proporcioné el ConfigService en TestBed de la siguiente manera:

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

Por lo tanto, siempre que la firma de getAppConfigValue sea ​​la misma que la especificada en el ConfigService real, lo que hace la función internamente puede modificarse.

Guillermo
fuente