jasmine.createSpyObj พร้อมคุณสมบัติ

Oct 28 2020

เมื่อจำลองการอ้างอิงในการทดสอบเชิงมุมของฉันฉันมักจะสร้างวัตถุสอดแนมโดยใช้jasmine.createSpyObj:

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

จากนั้นให้ไปที่ TestBed:

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

เมื่อฉันใช้ในการทดสอบฉันสามารถระบุค่าส่งคืนที่ต้องการได้:

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

ตอนนี้ฉันต้องล้อเลียนคุณสมบัติด้วยและฉันไม่สามารถหาคำตอบได้ว่ามันควรจะทำอย่างไร createSpyObjไม่อนุญาตให้มีการกำหนดชื่อคุณสมบัติ:

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

แต่ฉันได้ลองใช้วิธีแก้ปัญหาที่แตกต่างกันไปตามบทความและคำตอบมากมายโดยไม่ประสบความสำเร็จเช่น:

// 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;  

วิธีเดียวที่ฉันจะทำให้มันทำงานได้ครึ่งหนึ่งคือ:

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

ปัญหาคือมันเป็นการสร้างครั้งเดียว หากฉันต้องการเปลี่ยนค่าที่คาดหวังในการทดสอบก็ไม่ได้ผล

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

มีวิธีแก้ปัญหาทั้งวิธีการจำลองและคุณสมบัติโดยการสร้างวัตถุสอดแนมหรือไม่หรือฉันควรสร้างคลาสปลอมของตัวเองที่ฉันสามารถใช้spyOnและspyOnProperty?

ฉันต้องการทราบด้วยว่าการใช้อาร์เรย์คุณสมบัติในcreateSpyObjคำจำกัดความคืออะไร จนถึงตอนนี้ฉันยังไม่เห็นตัวอย่างใด ๆ บนเว็บที่อธิบายเรื่องนี้

คำตอบ

5 jonrsharpe Oct 28 2020 at 01:29

ตามเอกสาร (เน้นของฉัน):

createSpyObjคุณสามารถสร้างวัตถุสายลับกับหลายคุณสมบัติในการได้อย่างรวดเร็วโดยผ่านอาร์เรย์หรือกัญชาของคุณสมบัติเป็นอาร์กิวเมนต์ที่สามไป ในกรณีนี้คุณจะไม่มีการอ้างอิงถึงสายลับที่สร้างขึ้นดังนั้นหากคุณต้องการเปลี่ยนกลยุทธ์การสอดแนมในภายหลังคุณจะต้องใช้Object.getOwnPropertyDescriptorวิธีการดังกล่าว

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

คุณสมบัติที่สอดแนมเป็นตัวบอก (ดูเช่นObject.definePropertyใน MDN ) ดังนั้นในการเข้าถึงวัตถุสอดแนมคุณต้องได้รับวัตถุตัวอธิบายจากนั้นโต้ตอบกับgetและsetวิธีการที่กำหนดไว้


ใน TypeScript คอมไพเลอร์ต้องการความช่วยเหลือเล็กน้อย createSpyObjส่งกลับอย่างใดอย่างหนึ่งanyหรือSpyObj<T>และSpyObjเพียงกำหนดวิธีการที่สอดแนม:

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

ดังนั้นในการเข้าถึง.andgetter ของ descriptor คุณจะต้องมีการผูกมัดตัวเลือก (ตามที่Object.getOwnPropertyDescriptorอาจส่งคืนundefined) และการยืนยันประเภทกับSpy:

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

สนามเด็กเล่น

1 IanGrainger Nov 11 2020 at 17:57

ขอบคุณมาก @jonrsharpe! ฉันเพิ่งเพิ่มฟังก์ชั่นเพื่อให้ฉันสามารถทำได้:

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

ซึ่งกำหนดเป็น:

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