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


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.