programing

Jasmine에서 프로그래밍 방식으로 스파이를 어떻게 제거합니까?

nasanasas 2020. 10. 28. 08:10
반응형

Jasmine에서 프로그래밍 방식으로 스파이를 어떻게 제거합니까?


프로그래밍 방식으로 재스민 테스트 스위트의 스파이를 어떻게 제거합니까? 감사.

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

좋은 아이디어인지 확실하지 않지만 단순히 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');
    })

})

그러나 스파이의 다른 행동이 필요한 경우에 새로운 제품군을 만드는 것이 더 나은 아이디어 일 수 있습니다.


로 설정 isSpy하는 false것은 매우 나쁜 생각입니다. 스파이를 감시하고 Jasmine이 사양이 끝날 때 스파이를 제거하면 원래 방법을 얻지 못하기 때문입니다. 방법은 첫 번째 스파이와 동일합니다.

이미 메서드를 감시하고 있고 원래 메서드를 대신 호출 andCallThrough()하려면 첫 번째 스파이 동작을 재정의하는 호출해야합니다 .

예를 들면

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

당신은 전화로 모든 스파이를 지울 수 있습니다 this.removeAllSpies()( this- 사양)


나는 그것이 .reset ()무엇인지 생각합니다 .

spyOn($, 'ajax');

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

expect($.ajax).toHaveBeenCalled();

$.ajax.calls.reset()

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

따라서 스파이는 사양간에 자동으로 재설정됩니다.

andCallFake()a 내에서 사용한 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!

Jasmine 2에서 스파이 상태는 SpyStrategy 인스턴스에 보관됩니다. 이 인스턴스를 호출 할 수 있습니다 $.ajax.and. GitHub에서 Jasmine 소스 코드를 참조하십시오 .

따라서 다른 가짜 방법을 설정하려면 다음을 수행하십시오.

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

원래 방법으로 재설정하려면 다음을 수행하십시오.

$.ajax.and.callThrough();

이것은 Jasmine 2.5에서 나를 위해 일하여 mock ajax의 재설정을 허용했습니다.

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

Then you can call it multiple times without error. spyOnAjax(mock1); spyOnAjax(mock2);


Or you can do it

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

})

In this case you use the same Spy but just change the var that it will return..


I'm posting this answer to address the comment in OP @Tri-Vuong's code - which was my main reason for my visiting this page:

I want to override the spy ... here and do it a little differently

None of the answers so far address this point, so I'll post what I've learned and summarize the other answers as well.

@Alissa called it correctly when she explained why it is a bad idea to set isSpy to false - effectively spying on a spy resulting in the auto-teardown behavior of Jasmine no longer functioning as intended. Her solution (placed within the OP context and updated for Jasmine 2+) was as follows:

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

The last it test demonstrates how one could change the behavior of an existing spy to something else besides original behavior: "and-declare" the new behavior on the spyObj previously stored in the variable in the beforeEach(). The first test illustrates my use case for doing this - I wanted a spy to behave a certain way for most of the tests, but then change it for a few tests later.

For earlier versions of Jasmine, change the appropriate calls to .andCallFake(, .andCallThrough(), and .andReturnValue( respectively.


From jasmine 2.5, you can use this global setting to update a spy within your test cases:

jasmine.getEnv().allowRespy(true);

just set the spy method to null

mockedService.spiedMethod = null;

참고URL : https://stackoverflow.com/questions/8885591/how-do-we-clear-spy-programmatically-in-jasmine

반응형