Jak szpiegować właściwość value (zamiast metody) za pomocą Jasmine

115

Jasmine spyOndobrze jest zmieniać zachowanie metody, ale czy istnieje sposób na zmianę właściwości value (zamiast metody) obiektu? kod mógłby wyglądać jak poniżej:

spyOn(myObj, 'valueA').andReturn(1);
expect(myObj.valueA).toBe(1);
Shuping
źródło

Odpowiedzi:

97

W lutym 2017 roku połączyli PR dodając tę ​​funkcję, wydali w kwietniu 2017.

więc aby szpiegować getters / setters, których używasz: const spy = spyOnProperty(myObj, 'myGetterName', 'get'); gdzie myObj jest twoją instancją, 'myGetterName' to nazwa tego zdefiniowanego w twojej klasie as, get myGetterName() {}a trzeci parametr to typ getlub set.

Możesz użyć tych samych twierdzeń, których już używasz w przypadku szpiegów utworzonych w programie spyOn.

Możesz więc na przykład:

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.

Oto wiersz w kodzie źródłowym github, w którym ta metoda jest dostępna, jeśli jesteś zainteresowany.

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

Odpowiadając na pierwotne pytanie, z jaśminem 2.6.1:

const spy = spyOnProperty(myObj, 'valueA', 'get').andReturn(1);
expect(myObj.valueA).toBe(1);
expect(spy).toHaveBeenCalled();
Juan
źródło
7
Jak to zrobić, jeśli valueAjest Observablelub Subject? DostajęProperty valueA does not have access type get
Ka Mok
4
To prawdopodobnie oznacza, że ​​nie możesz go używać na tej nieruchomości. spyOnProperty używa getOwnPropertyDescriptor i sprawdza, czy istnieje typ dostępu get | set dla tego deskryptora właściwości. Otrzymujesz błąd, ponieważ deskryptor właściwości nie jest ustawiony lub nie jest pobierany dla właściwości, którą próbujesz szpiegować. Jasmine robi to: const descriptor = Object.getOwnPropertyDescriptor ... if (! Descriptor [accessType]) {// zgłoszony błąd}
Juan
Nie ma więc sposobu na szpiegowanie właściwości bez jawnych metod pobierających i ustawiających? Szpieg to właściwość używana.
Dominik
@Dominik Tutaj napisałem wszystkie związane z tym sprawy pod maską w dłuższym artykule: medium.com/@juanlizarazo/ ...
Juan
1
@Mathieu to nie jest dobre stwierdzenie, ponieważ potwierdza to, co już wiemy z tego, co powiedzieliśmy szpiegowi, ale właśnie o to pytali i ten sam fragment kodu pytania ¯_ (ツ) _ / ¯ Rzecz ich pytaniem było, jak szpiegować nieruchomość, a nie konkretny przykład.
Juan
12

Jest jakiś powód, dla którego nie możesz po prostu zmienić tego bezpośrednio na obiekcie? To nie jest tak, że javascript wymusza widoczność właściwości na obiekcie.

Paul Harris
źródło
1
użycie spyOnjawnie wskazuje, że chcę coś mockować, podczas gdy ja bezpośrednio ustawiam właściwość niejawnie wskazuje, że chcę coś mock, i nie jestem pewien, czy ktoś inny zrozumie, że kpię z czegoś, gdy czyta kod. Innym przypadkiem jest to, że nie chcę zmieniać wewnętrznego zachowania obiektu, na przykład jeśli zmienię właściwość length dla tablicy, tablica jest przycinana, więc makieta będzie lepsza
Shuping
@Shuping, w tym przypadku nie kpisz. Byłbyś skubany, co jest całkowicie w porządku. Będziesz „zmieniać zachowanie” tylko w trakcie testu, czyli to, co starałeś się osiągnąć za pomocą spyOn.
Fabio Milheiro
Prawdopodobnie chcesz szpiegować prototyp obiektu środowiska wykonawczego, aby upewnić się, że właściwość będzie istniała w czasie wykonywania. Jasmine spyOnnie zdaje testu, jeśli nieruchomość nie istnieje.
sennett
2
Jednym z przykładów jest ustawienie lub usunięcie window.sessionStorage:TypeError: Cannot assign to read only property 'sessionStorage' of object '#<Window>'
Chris Sattinger
2
Nie zawsze jest możliwe ponowne przypisanie właściwości obiektu w javascript
Renaud
12

Jasmine nie ma takiej funkcji, ale możesz zhakować coś razem za pomocą Object.defineProperty.

Możesz refaktoryzować swój kod, aby użyć funkcji getter, a następnie szpiegować getter.

spyOn(myObj, 'getValueA').andReturn(1);
expect(myObj.getValueA()).toBe(1);
Eric
źródło
Tak, na razie to mogę zrobić.
Shuping
4
dla jaśminu 2 to:and.returnValue(1)
jtzero
9

Najlepszym sposobem jest użycie spyOnProperty. Oczekuje 3 parametrów i musisz przekazać getlub setjako trzeci parametr.

Przykład

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

Ja tu ustawienie getod clientWidthod div.nativeElementobiektu.

Aniruddha Das
źródło
6

Jeśli używasz ES6 (Babel) lub TypeScript, możesz usunąć właściwość za pomocą metod dostępu get i set

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

Następnie w swoim teście możesz sprawdzić, czy właściwość jest ustawiona za pomocą:

stub.valueA = 'foo';

expect(stub.setValueA).toHaveBeenCalledWith('foo');
Jaskółka oknówka
źródło
Lub, jeśli metody pobierające są częścią testowanej klasy, kody pośredniczące mogą zostać wstrzyknięte do podklasy.
ccprog
6

Właściwym sposobem na to jest szpiegowanie właściwości, co pozwoli na zasymulowanie właściwości obiektu o określonej wartości.

const spy = spyOnProperty(myObj, 'valueA').and.returnValue(1);
expect(myObj.valueA).toBe(1);
expect(spy).toHaveBeenCalled();
alexalejandroem
źródło
1

Załóżmy, że istnieje metoda podobna do tej, która wymaga przetestowania. srcWłaściwość małego obrazu wymaga sprawdzenia

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

Poniższa funkcja spyOn () powoduje podanie "nowego obrazu" fałszywego kodu z testu kod spyOn zwraca obiekt, który ma tylko właściwość src

Ponieważ zmienna "hook" jest widoczna w fałszywym kodzie w SpyOn, a także później po wywołaniu "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');
    });

To jest dla jaśminu 1.3, ale może działać na 2.0, jeśli "andCallFake" zostanie zmieniona na nazwę 2.0

Vorsprung
źródło
1

Używam siatki kendo i dlatego nie mogę zmienić implementacji na metodę pobierającą, ale chcę testować wokół tego (mockowanie siatki), a nie testować samej siatki. Używałem obiektu szpiegowskiego, ale to nie obsługuje mockowania właściwości, więc robię to:

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

Jest trochę rozwlekły, ale działa świetnie

jolySoft
źródło
0

Jestem trochę spóźniony na imprezę, którą znam, ale

Możesz uzyskać bezpośredni dostęp do obiektu wywołań, który może dostarczyć zmienne dla każdego wywołania

expect(spy.calls.argsFor(0)[0].value).toBe(expectedValue)
CosmicChild
źródło
-3

Nie możesz mockować zmiennej, ale możesz utworzyć dla niej funkcję pobierającą i mockować tę metodę w swoim pliku specyfikacji.

Gampesh
źródło