Czy jest jakiś sposób na zmodyfikowanie szpiegów Jasmine w oparciu o argumenty?

146

Mam funkcję, którą chciałbym przetestować, która dwukrotnie wywołuje metodę zewnętrznego interfejsu API, używając różnych parametrów. Chciałbym wyśmiewać ten zewnętrzny interfejs API za pomocą szpiega Jasmine i zwracać różne rzeczy w oparciu o parametry. Czy można to zrobić w Jasmine? Najlepsze, co mogę wymyślić, to hack przy użyciu 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
źródło

Odpowiedzi:

213

W wersjach Jasmine 3.0 i nowszych możesz używać 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);
  });
});

W przypadku wersji Jasmine starszych niż 3.0 callFakejest to właściwy sposób, ale można to uprościć, używając obiektu do przechowywania wartości zwracanych

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

W zależności od wersji Jasmine składnia jest nieco inna:

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

Zasoby:

Andreas Köberle
źródło
11
To jest teraz and.callFake- jasmine.github.io/2.2/… >
Lucy Bain
Musiałem zwrócić różne obietnice, więc zwrot wyglądał nieco inaczej: return q.when (params [myParam]) ;. W przeciwnym razie było to miejsce na rozwiązanie mojego problemu. Moim wymarzonym rozwiązaniem byłaby zmiana połączeń „and.returnValue”.
Bill Turner
7
wydaje się, że jaśmin powinien mieć lepszy sposób na zadeklarowanie tego. Polub spyOn(fake, 'method').withArgs('abc').and.returnValue('Jane')i spyOn(fake, 'method').withArgs('123').and.returnValue(98765).
jrharshath
@jrharshath .withArgsnie działa na mnie w jaśminowym 2,0
hemkaran_raghav
1
.withArgsnie jest naprawdę dostępna - miałem na myśli, że taka metoda miałaby sens przy pisaniu testów.
jrharshath,
9

Możesz także użyć $providedo stworzenia szpiega. I mock używać and.returnValueszamiast and.returnValueprzekazywania danych sparametryzowanych.

Zgodnie z dokumentacją Jasmine: łącząc szpiega z and.returnValues, wszystkie wywołania funkcji będą zwracać określone wartości w kolejności, aż osiągnie koniec listy wartości zwracanych, po czym zwróci wartość undefined dla wszystkich kolejnych wywołań.

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
źródło
To jest prawidłowa odpowiedź, ponieważ test powinien zawsze dokładnie wiedzieć, jak zostanie wywołany szpieg, a zatem powinien po prostu używać go returnValuesdo obsługi wielu połączeń
Schmuli
2
Aby wyjaśnić odpowiedź akhouri: ta metoda działa tylko wtedy, gdy externalApi.get.and.returnValues('abc','123')wywoływana jest w itfunkcji. W przeciwnym razie, jeśli ustawisz listę wartości, w innym przypadku, to nigdy nie zadziała, ponieważ kolejność przeprowadzania testów nie jest przewidywalna. W rzeczywistości testy nie powinny zależeć od kolejności, w jakiej są wykonywane.
avi.elkharrat
0

W moim przypadku miałem komponent, który testowałem, aw jego konstruktorze jest usługa konfiguracyjna z metodą o nazwie getAppConfigValue, która jest wywoływana dwukrotnie, za każdym razem z różnymi argumentami:

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

W mojej specyfikacji podałem ConfigService w TestBed w następujący sposób:

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

Tak więc, o ile podpis dla getAppConfigValue jest taki sam, jak określony w rzeczywistej usłudze ConfigService, to, co funkcja robi wewnętrznie, można zmodyfikować.

Guillermo
źródło