Cách do thám Trên một thuộc tính giá trị (chứ không phải là một phương pháp) với Jasmine


115

Jasmine's spyOnrất tốt để thay đổi hành vi của một phương thức, nhưng có cách nào để thay đổi một thuộc tính giá trị (chứ không phải là một phương thức) cho một đối tượng không? mã có thể như sau:

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

Câu trả lời:


97

Vào tháng 2 năm 2017, họ đã hợp nhất một PR thêm tính năng này, họ đã phát hành vào tháng 4 năm 2017.

vì vậy để theo dõi getters / setters bạn sử dụng: const spy = spyOnProperty(myObj, 'myGetterName', 'get'); trong đó myObj là phiên bản của bạn, 'myGetterName' là tên của đối tượng đó được định nghĩa trong lớp của bạn get myGetterName() {}và tham số thứ ba là loại gethoặc set.

Bạn có thể sử dụng cùng các xác nhận mà bạn đã sử dụng với các gián điệp được tạo spyOn.

Vì vậy, bạn có thể ví dụ:

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.

Đây là dòng trong mã nguồn github nơi có phương pháp này nếu bạn quan tâm.

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

Trả lời câu hỏi ban đầu, với jasmine 2.6.1, bạn sẽ:

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

7
Làm cách nào để làm điều này nếu valueAlà một Observablehoặc Subject? Tôi đang nhậnProperty valueA does not have access type get
Ka Mok

4
Điều đó có thể có nghĩa là bạn không thể sử dụng nó trên thuộc tính đó. spyOnProperty đang sử dụng getOwnPropertyDescriptor và kiểm tra xem loại truy cập get | set cho bộ mô tả thuộc tính đó có tồn tại hay không. Lỗi bạn nhận được là do bộ mô tả thuộc tính không được đặt hoặc nhận được thuộc tính mà bạn đang cố gắng theo dõi. Jasmine thực hiện điều này: const descriptor = Object.getOwnPropertyDescriptor ... if (! Descriptor [accessType]) {// lỗi được ném ra}
Juan

Vì vậy, không có cách nào để theo dõi một thuộc tính mà không có các phương thức getter và setter rõ ràng? Để do thám là một tài sản được sử dụng.
Dominik

@Dominik Ở đây tôi đã viết tất cả những thứ liên quan đến vấn đề này trong một bài báo dài hơn: medium.com/@juanlizarazo/…
Juan

1
@Mathieu, đây không phải là một khẳng định hay vì nó chỉ khẳng định những gì chúng ta đã biết từ những gì chúng ta đã yêu cầu gián điệp làm, nhưng đó chỉ là những gì họ đang hỏi và đoạn mã câu hỏi rất giống nhau của họ ¯_ (ツ) _ / ¯ Vấn đề câu hỏi của họ là làm thế nào để theo dõi một tài sản và không quá nhiều ví dụ cụ thể của họ.
Juan

12

Bất kỳ lý do gì bạn không thể chỉ thay đổi nó trên đối tượng trực tiếp? Nó không giống như thể javascript thực thi khả năng hiển thị của một thuộc tính trên một đối tượng.


1
bằng cách sử dụng spyOnchỉ rõ ràng rằng tôi muốn chế nhạo một cái gì đó, trong khi tôi trực tiếp đặt thuộc tính ngầm chỉ ra rằng tôi muốn giả một cái gì đó và tôi không chắc ai đó sẽ hiểu rằng tôi đang chế nhạo một cái gì đó khi anh ta đang đọc mã. Trường hợp khác là tôi không muốn thay đổi hành vi bên trong của đối tượng, ví dụ nếu tôi thay đổi thuộc tính length cho một mảng, mảng được cắt, do đó, một mô hình sẽ tốt hơn
Shuping

@Shuping, trong trường hợp này, bạn sẽ không chế giễu. Bạn sẽ bị đơ, điều đó hoàn toàn ổn. Bạn sẽ chỉ "thay đổi hành vi" trong bài kiểm tra, đó là những gì bạn đang cố gắng đạt được với spyOn.
Fabio Milheiro

Có thể bạn muốn theo dõi nguyên mẫu đối tượng thời gian chạy để đảm bảo thuộc tính sẽ tồn tại trong thời gian chạy. Jasmine spyOnthất bại trong bài kiểm tra nếu tài sản không tồn tại.
sennett

2
Một ví dụ là thiết lập hoặc xóa window.sessionStorage:TypeError: Cannot assign to read only property 'sessionStorage' of object '#<Window>'
Chris Sattinger

2
Nó không phải là luôn luôn có thể chuyển nhượng lại một tài sản đối tượng trong javascript
Renaud

12

Jasmine không có chức năng đó, nhưng bạn có thể hack một thứ gì đó cùng nhau bằng cách sử dụng Object.defineProperty.

Bạn có thể cấu trúc lại mã của mình để sử dụng một hàm getter, sau đó theo dõi getter.

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

Vâng, đó là những gì tôi có thể làm bây giờ.
Shuping

4
cho hoa nhài 2 đó là:and.returnValue(1)
jtzero

9

Cách tốt nhất là sử dụng spyOnProperty. Nó yêu cầu 3 tham số và bạn cần chuyển gethoặc setnhư một tham số thứ ba.

Thí dụ

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

Ở đây tôi đang thiết lập getcủa clientWidthcác div.nativeElementđối tượng.


6

Nếu bạn đang sử dụng ES6 (Babel) hoặc TypeScript, bạn có thể khai báo thuộc tính bằng cách sử dụng trình truy cập get và set

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

Sau đó, trong thử nghiệm của mình, bạn có thể kiểm tra xem thuộc tính có được đặt bằng:

stub.valueA = 'foo';

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

Hoặc, nếu các getters là một phần của lớp đang được thử nghiệm, thì các phần khai thác có thể được đưa vào một lớp con.
ccprog

6

Cách thích hợp để làm điều này là với spy on property, nó sẽ cho phép bạn mô phỏng một thuộc tính trên một đối tượng với một giá trị cụ thể.

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

1

Giả sử có một phương pháp như thế này cần kiểm tra srcThuộc tính của hình ảnh nhỏ cần kiểm tra

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 () bên dưới khiến "Hình ảnh mới" được cung cấp mã giả từ quá trình kiểm tra mã spyOn trả về một đối tượng chỉ có thuộc tính src

Vì biến "hook" có phạm vi hiển thị trong mã giả trong SpyOn và sau đó sau khi "reportABCEvent" được gọi

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

Điều này dành cho jasmine 1.3 nhưng có thể hoạt động trên 2.0 nếu "andCallFake" được thay đổi thành tên 2.0


1

Tôi đang sử dụng lưới kiếm đạo và do đó không thể thay đổi cách triển khai thành phương pháp getter nhưng tôi muốn kiểm tra xung quanh điều này (mô phỏng lưới) chứ không phải kiểm tra chính lưới. Tôi đang sử dụng một đối tượng gián điệp nhưng điều này không hỗ trợ chế độ giả tài sản, vì vậy tôi thực hiện điều này:

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

Nó hơi dài dòng nhưng nó hoạt động tốt


0

Tôi biết đến bữa tiệc ở đây hơi muộn nhưng,

Bạn có thể truy cập trực tiếp vào đối tượng cuộc gọi, đối tượng này có thể cung cấp cho bạn các biến cho mỗi cuộc gọi

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

-3

Bạn không thể giả lập biến nhưng bạn có thể tạo hàm getter cho nó và giả lập phương thức đó trong tệp đặc tả của bạn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.