Как шпионить за ценным свойством (а не методом) с помощью Jasmine

115

Жасмин spyOnхорош для изменения поведения метода, но есть ли способ изменить свойство значения (а не метод) для объекта? код может быть таким, как показано ниже:

spyOn(myObj, 'valueA').andReturn(1);
expect(myObj.valueA).toBe(1);
Shuping
источник

Ответы:

97

В феврале 2017 года они объединили PR, добавив эту функцию, выпущенную в апреле 2017 года.

так что, чтобы следить за геттерами / сеттерами, которые вы используете: const spy = spyOnProperty(myObj, 'myGetterName', 'get'); где myObj - ваш экземпляр, myGetterName - это имя того, которое определено в вашем классе as, 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

Отвечая на исходный вопрос с помощью jasmine 2.6.1, вы должны:

const spy = spyOnProperty(myObj, 'valueA', 'get').andReturn(1);
expect(myObj.valueA).toBe(1);
expect(spy).toHaveBeenCalled();
Juan
источник
7
Как мне это сделать, если valueAесть Observableили Subject? Я получаюProperty valueA does not have access type get
Ka Mok
4
Это, вероятно, означает, что вы не можете использовать его на этом участке. spyOnProperty использует getOwnPropertyDescriptor и проверяет, существует ли тип доступа, установленный для этого дескриптора свойства. Вы получаете ошибку, потому что дескриптор свойства не установлен или не получен для свойства, за которым вы пытаетесь шпионить. Жасмин делает следующее: const descriptor = Object.getOwnPropertyDescriptor ... if (! Descriptor [accessType]) {// выдается ошибка}
Хуан,
Итак, нет возможности шпионить за свойством без явных методов получения и установки? Шпионить - это свойство.
Доминик
@Dominik Здесь я написал все, что связано с этим, в более длинной статье: medium.com/@juanlizarazo/…
Хуан,
1
@Mathieu, это не очень хорошее утверждение, потому что оно просто утверждает то, что мы уже знаем из того, что мы сказали шпиону сделать, но это именно то, что они просили, и их тот же самый фрагмент кода вопроса ¯_ (ツ) _ / ¯ Дело Их вопрос заключался в том, как шпионить за собственностью, а не столько в их конкретном примере.
Хуан
12

По какой причине вы не можете просто изменить это на объекте напрямую? Это не значит, что javascript обеспечивает видимость свойства объекта.

Пол Харрис
источник
1
using spyOnявно указывает, что я хочу что-то имитировать, тогда как я напрямую устанавливаю свойство, неявно указывает, что я хочу что-то имитировать, и я не уверен, что кто-то еще поймет, что я издеваюсь над чем-то, когда он читает код. Другой случай заключается в том, что я не хочу изменять внутреннее поведение объекта, например, если я изменяю свойство length для массива, массив обрезается, так что макет будет лучше
Shuping
@Shuping, в этом случае вы бы не издевались. Вы бы сделали заглушку, что совершенно нормально. Вы могли бы «изменить поведение» только внутри теста, чего вы и пытались достичь с помощью spyOn.
Фабио Мильейро
Возможно, вы захотите проследить за прототипом объекта среды выполнения, чтобы убедиться, что свойство будет существовать во время выполнения. Жасмин spyOnне проходит тест, если свойство не существует.
Sennett
2
Один из примеров - установить или удалить window.sessionStorage:TypeError: Cannot assign to read only property 'sessionStorage' of object '#<Window>'
Крис Саттингер,
2
Не всегда возможно переназначить свойство объекта в javascript
Renaud
12

Жасмин не имеет такой функции, но вы можете что-то вместе взломать, используя Object.defineProperty.

Вы можете реорганизовать свой код, чтобы использовать функцию получения, а затем шпионить за получателем.

spyOn(myObj, 'getValueA').andReturn(1);
expect(myObj.getValueA()).toBe(1);
Эрик
источник
Да, это то, что я могу сделать сейчас.
Shuping
4
для jasmine 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.

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');
Мартин
источник
Или, если геттеры являются частью тестируемого класса, заглушки могут быть введены в подкласс.
ccprog 01
6

Правильный способ сделать это - использовать свойство spy on, это позволит вам смоделировать свойство объекта с определенным значением.

const spy = spyOnProperty(myObj, 'valueA').and.returnValue(1);
expect(myObj.valueA).toBe(1);
expect(spy).toHaveBeenCalled();
Alexalejandroem
источник
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

Я использую сетку кендо и поэтому не могу изменить реализацию на метод получения, но я хочу протестировать это (имитируя сетку), а не тестировать саму сетку. Я использовал объект-шпион, но он не поддерживает насмешку над свойствами, поэтому я делаю следующее:

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

Это немного затянуто, но работает приятно

jolySoft
источник
0

Я немного опоздал на вечеринку, я знаю, но,

Вы можете напрямую получить доступ к объекту вызовов, который может предоставить вам переменные для каждого вызова

expect(spy.calls.argsFor(0)[0].value).toBe(expectedValue)
CosmicChild
источник
-3

Вы не можете имитировать переменную, но вы можете создать для нее функцию получения и смоделировать этот метод в своем файле спецификации.

Гампеш
источник