Adakah cara untuk memodifikasi mata-mata Jasmine berdasarkan argumen?

147

Saya memiliki fungsi yang ingin saya uji yang memanggil metode API eksternal dua kali, menggunakan parameter yang berbeda. Saya ingin mengejek API eksternal ini dengan mata-mata Jasmine, dan mengembalikan hal-hal berbeda berdasarkan parameter. Apakah ada cara untuk melakukan ini di Jasmine? Yang terbaik yang bisa saya lakukan adalah hack menggunakan 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
sumber

Jawaban:

215

Dalam Jasmine versi 3.0 dan di atasnya Anda dapat menggunakan 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);
  });
});

Untuk versi Jasmine lebih awal dari 3.0 callFakeadalah cara yang tepat, tetapi Anda bisa menyederhanakannya menggunakan objek untuk menyimpan nilai kembali

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

Tergantung pada versi Jasmine, sintaksinya sedikit berbeda:

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

Sumber:

Andreas Köberle
sumber
11
Ini sekarang and.callFake- jasmine.github.io/2.2/… >
Lucy Bain
Saya harus mengembalikan janji yang berbeda, jadi pengembaliannya terlihat sedikit berbeda: return q.when (params [myParam]) ;. Kalau tidak, ini adalah titik pada solusi untuk masalah saya. Solusi impian saya adalah mengubah panggilan "and.returnValue".
Bill Turner
7
Rasanya seperti melati harus memiliki cara yang lebih baik untuk menyatakan ini. Suka spyOn(fake, 'method').withArgs('abc').and.returnValue('Jane')dan spyOn(fake, 'method').withArgs('123').and.returnValue(98765).
jrharshath
@jrharshath .withArgstidak bekerja untuk saya di melati 2.0
hemkaran_raghav
1
.withArgstidak benar-benar tersedia - maksud saya metode seperti itu masuk akal ketika menulis tes.
jrharshath
9

Anda juga bisa menggunakan $provideuntuk membuat mata-mata. Dan mengejek menggunakan and.returnValuesbukannya and.returnValueuntuk meneruskan data parameter.

Sesuai Jasmine docs: Dengan merantai mata-mata dengan and.returnValues, semua panggilan ke fungsi akan mengembalikan nilai tertentu hingga mencapai akhir daftar nilai pengembalian, pada titik mana ia akan kembali tanpa ditentukan untuk semua panggilan berikutnya.

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
sumber
Ini adalah jawaban yang benar, karena tes harus selalu tahu persis bagaimana mata-mata akan dipanggil, dan oleh karena itu hanya perlu digunakan returnValuesuntuk mendukung beberapa panggilan
Schmuli
2
Hanya untuk memperjelas jawaban akhouri: metode ini hanya berfungsi ketika externalApi.get.and.returnValues('abc','123')dipanggil di dalam itfungsi. Kalau tidak, jika Anda menetapkan daftar nilai, di tempat lain, itu tidak akan pernah berhasil karena urutan di mana tes dijalankan tidak dapat diprediksi. Sebenarnya tes seharusnya tidak tergantung pada urutan pelaksanaannya.
avi.elkharrat
0

Dalam kasus saya, saya memiliki komponen yang saya uji dan, dalam konstruktornya, ada layanan konfigurasi dengan metode yang disebut getAppConfigValue yang disebut dua kali, setiap kali dengan argumen yang berbeda:

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

Dalam spec saya, saya memberikan ConfigService di TestBed seperti:

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

Jadi, selama tanda tangan untuk getAppConfigValue sama dengan yang ditentukan dalam ConfigService aktual, apa fungsi yang dilakukan secara internal dapat dimodifikasi.

Guillermo
sumber