jasmine.createSpyObj dengan properti

Oct 28 2020

Saat mengejek dependensi dalam tes Angular saya, saya biasanya membuat objek mata-mata menggunakan jasmine.createSpyObj:

const serviceSpy= jasmine.createSpyObj('MyService', ['method']);

lalu berikan ke TestBed:

  providers: [
    {provide: MyService, useValue: serviceSpy}
  ]

Saat saya menggunakannya dalam pengujian, saya kemudian dapat menentukan nilai pengembalian yang diinginkan:

serviceSpy.method.and.returnValue(of([...]));

Sekarang saya juga perlu mengejek properti dan saya tidak dapat mengetahui bagaimana itu harus dilakukan. createSpyObjtidak mengizinkan definisi nama properti:

const serviceSpy= jasmine.createSpyObj('MyService', ['method'], ['property']);

tetapi saya telah mencoba berbagai solusi berdasarkan banyak artikel dan jawaban di luar sana tanpa hasil, misalnya:

// Cannot read property 'and' of undefined    
serviceSpy.property.and.returnValue(true);  
// not declared configurable
spyOnProperty(serviceSpy, 'property').and.returnValue(true);  
// no build errors, but value stays 'undefined'
serviceSpy.property = true;  

Satu-satunya cara agar saya bisa membuatnya menjadi 'setengah' adalah:

let fakeValue = true;
const serviceSpy= jasmine.createSpyObj('MyService', ['method'], {'property': fakeValue});

Masalahnya di sini adalah bahwa ini adalah satu set waktu saat penciptaan. Jika saya ingin mengubah nilai yang diharapkan dalam tes, itu tidak berhasil.

fakeValue = false;
serviceSpy.property ==> stays to the initial value 'true';

Apakah ada solusi untuk metode dan properti tiruan dengan membuat objek mata-mata, atau haruskah saya membuat kelas palsu sendiri yang kemudian dapat saya gunakan spyOndan spyOnProperty?

Saya juga ingin tahu apa kegunaan dari array properti dalam createSpyObjdefinisi. Sejauh ini saya belum melihat contoh di web yang menjelaskannya.

Jawaban

5 jonrsharpe Oct 28 2020 at 01:29

Per dokumentasi (penekanan saya):

Anda dapat membuat objek mata-mata dengan beberapa properti dengan cepat dengan meneruskan array atau hash properti sebagai argumen ketiga createSpyObj. Dalam hal ini Anda tidak akan memiliki referensi ke mata-mata yang dibuat, jadi jika Anda perlu mengubah strategi mata-mata mereka nanti, Anda harus menggunakan Object.getOwnPropertyDescriptorpendekatan tersebut .

it("creates a spy object with properties", function() {
  let obj = createSpyObj("myObject", {}, { x: 3, y: 4 });
  expect(obj.x).toEqual(3);

  Object.getOwnPropertyDescriptor(obj, "x").get.and.returnValue(7);
  expect(obj.x).toEqual(7);
});

Properti yang dimata-matai adalah deskriptor (lihat misalnya Object.definePropertydi MDN ), jadi untuk mengakses objek mata-mata Anda perlu mendapatkan objek deskriptor kemudian berinteraksi dengan getdan setmetode yang ditentukan di atasnya.


Di TypeScript, kompilator membutuhkan sedikit bantuan. createSpyObjmengembalikan salah satu anyatau SpyObj<T>, dan SpyObjhanya mendefinisikan metode sebagai dimata-matai:

type SpyObj<T> = T & {
    [K in keyof T]: T[K] extends Func ? T[K] & Spy<T[K]> : T[K];
               // |     if it's a     |    spy on it     | otherwise leave
               // |     callable      |                  | it alone
};

Jadi untuk mengakses .andpengambil deskriptor, Anda memerlukan rangkaian opsional (as Object.getOwnPropertyDescriptormay return undefined) dan pernyataan tipe ke Spy:

(Object.getOwnPropertyDescriptor(obj, "x")?.get as Spy<() => number>).and.returnValue(7);

Tempat bermain

1 IanGrainger Nov 11 2020 at 17:57

Terima kasih banyak @jonrsharpe! Saya baru saja menambahkan fungsi sehingga saya bisa melakukan:

spyPropertyGetter(spy, 'propName').and.returnValue(...)

Yang didefinisikan sebagai:

function spyPropertyGetter(
  spyObj: jasmine.SpyObj<any>,
  propName: string
) {
  return Object.getOwnPropertyDescriptor(spyObj, propName).get as jasmine.Spy<
    jasmine.Func
  >;
}