Як ми програмно очищаємо шпигуна від Жасмину?


77

Як ми програмно очистимо шпигуна від набору тестів на жасмин? Дякую.

beforeEach(function() {
  spyOn($, "ajax").andCallFake(function(params){
  })
})

it("should do something", function() {
  //I want to override the spy on ajax here and do it a little differently
})

4
Ви впевнені, що вибрали правильну "правильну" відповідь?
hgoebl

Відповіді:


1

Я не впевнений, що це хороша ідея, але ви можете просто встановити isSpyпрапорець функції на false:

describe('test', function() {
    var a = {b: function() {
    }};
    beforeEach(function() {
        spyOn(a, 'b').andCallFake(function(params) {
            return 'spy1';
        })
    })
    it('should return spy1', function() {
        expect(a.b()).toEqual('spy1');
    })

    it('should return spy2', function() {
        a.b.isSpy = false;
        spyOn(a, 'b').andCallFake(function(params) {
            return 'spy2';
        })
        expect(a.b()).toEqual('spy2');
    })

})

Але, можливо, краща ідея створити новий набір для цього випадку, коли вам потрібна інша поведінка від вашого шпигуна.


2
Ідеальний Андреас. Якраз те, що мені потрібно. Якщо ви згодом з’ясуєте, гарна це ідея чи ні. Будь ласка, дай мені знати. Дякую. +1
Tri Vuong

2
це не найкраща ідея, див. мою відповідь.
FilmJ

3
це насправді не є гарною ідеєю, таким чином після закінчення роботи специфікації метод залишатиметься шпигуном замість оригінального методу див. відповідь Аліси
Леон Федотов

Підказка: У поточній версії жасмину потрібно використовувати "and.callFake (...)" замість "andCallFake (...)"
Ilker Cat

7
Це НЕ повинно бути прийнятою відповіддю! Будь ласка, перегляньте відповідь @ Alissa
Chris Knight

133

установка isSpyдля falseдуже поганий ідея, так як тоді ви шпигувати за шпигун , і коли Жасмин очищає шпигун в кінці вашої специфікації ви не отримаєте оригінальний метод. метод буде дорівнює першому шпигуну.

якщо ви вже шпигуєте за методом, і ви хочете, щоб замість цього був викликаний оригінальний метод, вам слід зателефонувати, andCallThrough()що замінить першу поведінку шпигуна.

наприклад

var spyObj = spyOn(obj,'methodName').andReturn(true);
spyObj.andCallThrough();

Ви можете очистити всіх шпигунів, зателефонувавши this.removeAllSpies()( this- специфікація)


1
Це найкраще рішення.
Matthieu Riegler

Я вважаю за краще сказати, що встановлення isSpy на false - це погана річ. Мені подобається нестандартне мислення Андреаса К.
KSev

22
зверніть увагу, що в жасмині 2 це spyObj.and.callThrough ();
Борис Шарпентьє

4
Якщо ви не хочете викликати вихідну функцію, а просто перевизначити оригінальний шпигун для вкладеного опису, зробіть це, повернувши інше значення, використовуючи "spyObj.and.returnValue ('якесь інше значення');"
Кот

2
З новим синтаксисом: spyOn(obj,'methodName').and.returnValue(true);встановіть шпигуна повертати значення. Тоді в наступному тесті ви хочете, щоб він шпигував реальну функцію так:obj.methodName.and.callThrough();
Годинникар

46

Я думаю, що для цього є .reset () :

spyOn($, 'ajax');

$.post('http://someUrl', someData);

expect($.ajax).toHaveBeenCalled();

$.ajax.calls.reset()

expect($.ajax).not.toHaveBeenCalled();

13
Отже, все це робить скидання стану відстеження, якщо ви хочете відновити поведінку за замовчуванням, це не допоможе.
FilmJ

19
Зверніть увагу, що це змінилося на mySpy.calls.reset()Жасмин 2.
Генрік Н

1
mySpy.calls.reset()скидає лічильник випадків, коли шпигуна викликали. Ви можете перевірити це за допомогоюexpect(spy).toHaveBeenCalledTimes(1)
Spiral Out

2
Як відповідь, яка абсолютно неправильно відповідає на питання, має майже 40 голосів проти? Питання задає питання, як замінити andCallFake, а не як скинути лічильник дзвінків.
Thor84no

20

Тож шпигуни автоматично скидаються між специфікаціями.

Ви насправді не отримуєте вигоди від "відновлення" оригінальної функції, якщо використовуєте її andCallFake()в межах, beforeEach()а потім намагаєтесь примусово змінити її в межах специфікації (імовірно, чому вона намагається завадити вам це зробити).

Тож будьте обережні, особливо якщо ваш шпигун налаштований на такий глобальний об’єкт, як jQuery.

Демонстрація:

var a = {b:function() { return 'default'; } }; // global scope (i.e. jQuery)
var originalValue = a.b;

describe("SpyOn test", function(){
  it('should return spy1', function(){
    spyOn(a, 'b').andCallFake(function(params) {
      return 'spy1';
    })
    expect(a.b()).toEqual('spy1');
  });

  it('should return default because removeAllSpies() happens in teardown', function(){
    expect(a.b()).toEqual('default');
  });


  it('will change internal state by "forcing" a spy to be set twice, overwriting the originalValue', function(){
    expect(a.b()).toEqual('default');

    spyOn(a, 'b').andCallFake(function(params) {
      return 'spy2';
    })
    expect(a.b()).toEqual('spy2');

    // This forces the overwrite of the internal state
    a.b.isSpy = false;
    spyOn(a, 'b').andCallFake(function(params) {
      return 'spy3';
    })
    expect(a.b()).toEqual('spy3');

  });

  it('should return default but will not', function(){
    expect(a.b()).toEqual('default'); // FAIL

    // What's happening internally?
    expect(this.spies_.length).toBe(1);
    expect(this.spies_[0].originalValue).toBe(originalValue); // FAIL
  });

});

describe("SpyOn with beforeEach test", function(){
  beforeEach(function(){
    spyOn(a, 'b').andCallFake(function(params) {
      return 'spy1';
    })
  })

  it('should return spy1', function(){
    // inspect the internal tracking of spies:
    expect(this.spies_.length).toBe(1);
    expect(this.spies_[0].originalValue).toBe(originalValue);

    expect(a.b()).toEqual('spy1');
  });

  it('should return spy2 when forced', function(){
    // inspect the internal tracking of spies:
    expect(this.spies_.length).toBe(1);
    expect(this.spies_[0].originalValue).toBe(originalValue);

    // THIS EFFECTIVELY changes the "originalState" from what it was before the beforeEach to what it is now.
    a.b.isSpy = false;
    spyOn(a, 'b').andCallFake(function(params) {
        return 'spy2';
    })
    expect(a.b()).toEqual('spy2');
  });

  it('should again return spy1 - but we have overwritten the original state, and can never return to it', function(){
    // inspect the internal tracking of spies:
    expect(this.spies_.length).toBe(1);
    expect(this.spies_[0].originalValue).toBe(originalValue); // FAILS!

    expect(a.b()).toEqual('spy1');
  });
});

// If you were hoping jasmine would cleanup your mess even after the spec is completed...
console.log(a.b == originalValue) // FALSE as you've already altered the global object!

Я намагався з’ясувати, що мені потрібно зробити, щоб перестати шпигувати за об’єктом. Ваша відповідь була дуже чіткою та корисною. І жасминові скелі для автоматичного догляду за цим, руйнуючи.
xverges

9

У Jasmine 2 держава-шпигун знаходиться в екземплярі SpyStrategy. Ви можете отримати цей екземпляр виклику $.ajax.and. Дивіться вихідний код Jasmine на GitHub .

Отже, щоб встановити інший фальшивий метод, зробіть наступне:

$.ajax.and.callFake(function() { ... });

Щоб скинути початковий метод, зробіть наступне:

$.ajax.and.callThrough();

2
Я вважаю, що $.ajax.and.andCallThrough();це не правильно. Має бути$.ajax.and.callThrough();
kriskodzi

Це спрацювало для мене; всередині beforeEach: spyOn(Foobar, 'getFoo').and.returnValue('generic'); }то всередині it: Foobar.getFoo.and.returnValue('special'). Дякую!
jakub.g

це правильна відповідь. я роблю те саме. Я отримав ідею з цього допису: github.com/jasmine/jasmine/issues/160 . Я не розумію, чому це не головна відповідь?
c_breeez

7

Це спрацювало для мене в Jasmine 2.5, щоб дозволити повторне встановлення макетного аяксу.

function spyOnAjax(mockResult) {
    // must set to true to allow multiple calls to spyOn:
    jasmine.getEnv().allowRespy(true);

    spyOn($, 'ajax').and.callFake(function () {
        var deferred = $.Deferred();
        deferred.resolve(mockResult);
        return deferred.promise();
    });
}

Тоді ви можете зателефонувати йому кілька разів без помилок. spyOnAjax (макет1); spyOnAjax (макет2);


2

З жасмину 2.5 ви можете використовувати це загальне налаштування для оновлення шпигуна у ваших тестових випадках:

jasmine.getEnv().allowRespy(true);

1

Або ви можете це зробити

describe('test', function() {
    var a, c;
    c = 'spy1';
    a = {
      b: function(){}
    };

    beforeEach(function() {
        spyOn(a, 'b').and.callFake(function () {
             return c;
        });
    })

    it('should return spy1', function() {
        expect(a.b()).toEqual('spy1');
    })

    it('should return spy2', function() {
        c = 'spy2';
        expect(a.b()).toEqual('spy2');
    })

})

У цьому випадку ви використовуєте того самого шпигуна, але просто змініть var, який він поверне ..


1

Я розміщую цю відповідь, щоб звернутися до коментаря в коді OP @ Tri-Vuong - що було моєю основною причиною мого відвідування цієї сторінки:

Я хочу замінити шпигуна ... тут і зробити це трохи інакше

Жодна з відповідей на даний момент не стосується цього питання, тому я опублікую те, що дізнався, та також підсумую інші відповіді.

@Alissa правильно називається, коли вона пояснює , чому це погана ідея встановити isSpyв false- ефективно шпигувати шпигун , що стався в авто-демонтажі Жасміна більше не функціонують належним чином . Її рішення (розміщене в контексті OP та оновлене для Jasmine 2+) було таким:

beforeEach(() => {
  var spyObj = spyOn(obj,'methodName').and.callFake(function(params){
  }) // @Alissa's solution part a - store the spy in a variable
})

it("should do the declared spy behavior", () => {
  // Act and assert as desired
})

it("should do what it used to do", () => {
  spyObj.and.callThrough(); // @Alissa's solution part b - restore spy behavior to original function behavior
  // Act and assert as desired
})

it("should do something a little differently", () => {
  spyObj.and.returnValue('NewValue'); // added solution to change spy behavior
  // Act and assert as desired
})

Останній itтест демонструє, як можна змінити поведінку існуючого шпигуна на щось інше, крім оригінальної поведінки: " and-declare" нову поведінку на spyObj, що раніше зберігалася у змінній у beforeEach(). Перший тест ілюструє мій варіант використання - я хотів, щоб шпигун поводився певною мірою для більшості тестів, але потім змінив його на кілька тестів пізніше.

Для більш ранніх версій жасмину, змініть відповідні виклики .andCallFake(, .andCallThrough()і .andReturnValue(відповідно.


-1

просто встановіть для шпигунського методу значення null

mockedService.spiedMethod = null;
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.