วิธีการสอดแนมบนคุณสมบัติมูลค่า (แทนที่จะเป็นวิธีการ) ด้วยจัสมิน


115

จัสมินspyOnเป็นสิ่งที่ดีในการเปลี่ยนพฤติกรรมของวิธีการ แต่มีวิธีใดในการเปลี่ยนคุณสมบัติค่า (แทนที่จะเป็นวิธีการ) สำหรับวัตถุหรือไม่? รหัสอาจเป็นดังนี้:

spyOn(myObj, 'valueA').andReturn(1);
expect(myObj.valueA).toBe(1);

คำตอบ:


97

ในเดือนกุมภาพันธ์ 2017 พวกเขาได้รวมการประชาสัมพันธ์เพื่อเพิ่มคุณสมบัตินี้ซึ่งเปิดตัวในเดือนเมษายน 2017

เพื่อสอดแนม getters / setters ที่คุณใช้: const spy = spyOnProperty(myObj, 'myGetterName', 'get'); โดยที่ myObj เป็นอินสแตนซ์ของคุณ 'myGetterName' คือชื่อที่กำหนดไว้ในคลาสของคุณget myGetterName() {}และพารามิเตอร์ตัวที่สามคือประเภทgetหรือset .

spyOnคุณสามารถใช้ยืนยันเดียวกันกับที่คุณใช้กับสายลับที่สร้างขึ้นด้วย

ตัวอย่างเช่นคุณสามารถ:

const spy = spyOnProperty(myObj, 'myGetterName', 'get'); // to stub and return nothing. Just spy and stub.
const spy = spyOnProperty(myObj, 'myGetterName', 'get').and.returnValue(1); // to stub and return 1 or any value as needed.
const spy = spyOnProperty(myObj, 'myGetterName', 'get').and.callThrough(); // Call the real thing.

นี่คือบรรทัดในซอร์สโค้ด github ที่มีวิธีนี้หากคุณสนใจ

https://github.com/jasmine/jasmine/blob/7f8f2b5e7a7af70d7f6b629331eb6fe0a7cb9279/src/core/requireInterface.js#L199

ตอบคำถามเดิมด้วยดอกมะลิ 2.6.1 คุณจะ:

const spy = spyOnProperty(myObj, 'valueA', 'get').andReturn(1);
expect(myObj.valueA).toBe(1);
expect(spy).toHaveBeenCalled();

7
ฉันจะทำสิ่งนี้ได้อย่างไรถ้าvalueAเป็นObservableหรือSubject? กำลังเดินทางProperty valueA does not have access type get
กะหมอก

4
นั่นอาจหมายความว่าคุณไม่สามารถใช้กับคุณสมบัตินั้นได้ spyOnProperty กำลังใช้ getOwnPropertyDescriptor และตรวจสอบว่าประเภทการเข้าถึง get | set สำหรับตัวบอกคุณสมบัตินั้นมีอยู่หรือไม่ ข้อผิดพลาดที่คุณได้รับเนื่องจากไม่ได้ตั้งค่าตัวบอกคุณสมบัติหรือได้รับสำหรับคุณสมบัติที่คุณพยายามสอดแนม จัสมินทำสิ่งนี้: const descriptor = Object.getOwnPropertyDescriptor ... if (! descriptor [accessType]) {// error is throw}
Juan

ดังนั้นไม่มีทางที่จะสอดแนมทรัพย์สินโดยไม่มีวิธีการรับและเซ็ตเตอร์อย่างชัดเจน? เพื่อสอดแนมเป็นทรัพย์สินที่ใช้
Dominik

@Dominik ที่นี่ฉันเขียนเนื้อหาทั้งหมดที่เกี่ยวข้องกับเรื่องนี้ในบทความที่ยาวกว่า: medium.com/@juanlizarazo/…
Juan

1
@Mathieu ไม่ใช่การยืนยันที่ดีเพราะเพียงแค่ยืนยันสิ่งที่เรารู้อยู่แล้วจากสิ่งที่เราบอกให้สายลับทำ แต่เป็นเพียงสิ่งที่พวกเขาถามและตัวอย่างรหัสคำถามที่เหมือนกันมาก¯_ (ツ) _ / ¯ประเด็น คำถามของพวกเขาคือการสอดแนมทรัพย์สินอย่างไรและไม่ใช่ตัวอย่างเฉพาะของพวกเขามากนัก
Juan

12

เหตุผลใดที่คุณไม่สามารถเปลี่ยนมันบนวัตถุได้โดยตรง? ไม่เหมือนกับว่าจาวาสคริปต์บังคับให้มองเห็นคุณสมบัติบนวัตถุ


1
การใช้spyOnบ่งบอกอย่างชัดเจนว่าฉันต้องการเยาะเย้ยอะไรบางอย่างในขณะที่ฉันตั้งค่าคุณสมบัติโดยตรงโดยปริยายบ่งบอกว่าฉันต้องการล้อเลียนอะไรบางอย่างและฉันไม่แน่ใจว่าคนอื่นจะเข้าใจว่าฉันกำลังล้อเลียนบางสิ่งบางอย่างเมื่อเขากำลังอ่านรหัส อีกกรณีคือฉันไม่ต้องการเปลี่ยนพฤติกรรมภายในของวัตถุเช่นถ้าฉันเปลี่ยนคุณสมบัติความยาวของอาร์เรย์อาร์เรย์จะถูกตัดออกดังนั้นการจำลองจะดีกว่า
Shuping

@Shuping ในกรณีนี้คุณจะไม่ล้อเลียน คุณจะดื้อซึ่งก็โอเค คุณจะ "เปลี่ยนพฤติกรรม" เฉพาะในการทดสอบซึ่งเป็นสิ่งที่คุณพยายามบรรลุด้วยspyOn.
Fabio Milheiro

คุณอาจต้องการสอดแนมต้นแบบวัตถุรันไทม์เพื่อให้แน่ใจว่าคุณสมบัติจะมีอยู่ในรันไทม์ จัสมินspyOnไม่ผ่านการทดสอบหากคุณสมบัติไม่มีอยู่จริง
sennett

2
ตัวอย่างหนึ่งคือการตั้งค่าหรือลบ window.sessionStorage:TypeError: Cannot assign to read only property 'sessionStorage' of object '#<Window>'
Chris Sattinger

2
ไม่สามารถกำหนดคุณสมบัติออบเจ็กต์ใหม่ได้เสมอไปในจาวาสคริปต์
มองซิเออร์

12

จัสมินไม่มีฟังก์ชั่นนั้น แต่คุณอาจแฮ็คบางอย่างร่วมกันได้โดยใช้ Object.definePropertyแต่คุณอาจจะสามารถที่จะตัดบางสิ่งบางอย่างร่วมกันโดยใช้

คุณสามารถ refactor โค้ดของคุณเพื่อใช้ฟังก์ชัน getter จากนั้นสอดแนมที่ getter

spyOn(myObj, 'getValueA').andReturn(1);
expect(myObj.getValueA()).toBe(1);

ใช่นั่นคือสิ่งที่ฉันทำได้ในตอนนี้
Shuping

4
สำหรับดอกมะลิ 2:and.returnValue(1)
jtzero

9

spyOnPropertyวิธีที่ดีที่สุดคือการใช้งาน คาดหวัง 3 พารามิเตอร์และคุณต้องผ่านgetหรือsetเป็นพารามิเตอร์ตัวที่สาม

ตัวอย่าง

const div = fixture.debugElement.query(By.css('.ellipsis-overflow'));
// now mock properties
spyOnProperty(div.nativeElement, 'clientWidth', 'get').and.returnValue(1400);
spyOnProperty(div.nativeElement, 'scrollWidth', 'get').and.returnValue(2400);

นี่ฉันกำลังตั้งค่าgetของclientWidthของdiv.nativeElementวัตถุ


6

หากคุณใช้ ES6 (Babel) หรือ TypeScript คุณสามารถดึงคุณสมบัติออกโดยใช้ get และ set accessors

export class SomeClassStub {
  getValueA = jasmine.createSpy('getValueA');
  setValueA = jasmine.createSpy('setValueA');
  get valueA() { return this.getValueA(); }
  set valueA(value) { this.setValueA(value); }
}

จากนั้นในการทดสอบของคุณคุณสามารถตรวจสอบว่าคุณสมบัติถูกตั้งค่าด้วย:

stub.valueA = 'foo';

expect(stub.setValueA).toHaveBeenCalledWith('foo');

หรือถ้า getters เป็นส่วนหนึ่งของคลาสที่อยู่ระหว่างการทดสอบก็สามารถฉีดต้นขั้วในคลาสย่อยได้
ccprog

6

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

const spy = spyOnProperty(myObj, 'valueA').and.returnValue(1);
expect(myObj.valueA).toBe(1);
expect(spy).toHaveBeenCalled();

1

สมมติว่ามีวิธีการเช่นนี้ที่ต้องทดสอบsrcคุณสมบัติของภาพขนาดเล็กจำเป็นต้องตรวจสอบ

function reportABCEvent(cat, type, val) {
                var i1 = new Image(1, 1);
                var link = getABC('creosote');
                    link += "&category=" + String(cat);
                    link += "&event_type=" + String(type);
                    link += "&event_value=" + String(val);
                    i1.src = link;
                }

spyOn () ด้านล่างทำให้ "รูปภาพใหม่" ถูกป้อนรหัสปลอมจากการทดสอบรหัส spyOn จะส่งคืนวัตถุที่มีคุณสมบัติ src เท่านั้น

เนื่องจากตัวแปร "hook" ถูกกำหนดขอบเขตให้มองเห็นได้ในโค้ดปลอมใน SpyOn และหลังจากนั้น "reportABCEvent" จะถูกเรียกว่า

describe("Alphabetic.ads", function() {
    it("ABC events create an image request", function() {
    var hook={};
    spyOn(window, 'Image').andCallFake( function(x,y) {
          hook={ src: {} }
          return hook;
      }
      );
      reportABCEvent('testa', 'testb', 'testc');
      expect(hook.src).
      toEqual('[zubzub]&arg1=testa&arg2=testb&event_value=testc');
    });

นี่ใช้สำหรับ Jasmine 1.3 แต่อาจใช้ได้กับ 2.0 ถ้า "andCallFake" ถูกเปลี่ยนเป็นชื่อ 2.0


1

ฉันใช้กริดเคนโดดังนั้นจึงไม่สามารถเปลี่ยนการใช้งานเป็นวิธี getter ได้ แต่ฉันต้องการทดสอบสิ่งนี้ (เยาะเย้ยกริด) และไม่ได้ทดสอบกริดเอง ฉันใช้วัตถุสอดแนม แต่สิ่งนี้ไม่สนับสนุนการล้อเลียนทรัพย์สินดังนั้นฉันจึงทำสิ่งนี้:

    this.$scope.ticketsGrid = { 
        showColumn: jasmine.createSpy('showColumn'),
        hideColumn: jasmine.createSpy('hideColumn'),
        select: jasmine.createSpy('select'),
        dataItem: jasmine.createSpy('dataItem'),
        _data: []
    } 

ค่อนข้างยืดเยื้อ แต่ใช้ได้ผลดี


0

ฉันไปงานปาร์ตี้ที่นี่ช้าไปหน่อย แต่ฉันรู้ว่า

คุณสามารถเข้าถึงวัตถุการโทรได้โดยตรงซึ่งสามารถให้ตัวแปรสำหรับการโทรแต่ละครั้ง

expect(spy.calls.argsFor(0)[0].value).toBe(expectedValue)

-3

คุณไม่สามารถเยาะเย้ยตัวแปรได้ แต่คุณสามารถสร้างฟังก์ชัน getter และจำลองวิธีการนั้นในไฟล์ข้อมูลจำเพาะของคุณได้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.