Javascript 我们如何在 Jasmine 中以编程方式清除间谍?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8885591/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 07:55:14  来源:igfitidea点击:

How do we clear spy programmatically in Jasmine?

javascriptjasmine

提问by Tri Vuong

How do we clear the spy in a jasmine test suite programmatically? Thanks.

我们如何以编程方式清除 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
})

采纳答案by Andreas K?berle

I'm not sure if its a good idea but you can simply set the isSpyflag on the function to false:

我不确定这是否是个好主意,但您可以简单地将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');
    })

})

But maybe its a better idea to create a new suite for this case where you need an other behavior from your spy.

但也许为这种情况创建一个新套件是一个更好的主意,在这种情况下,您需要间谍的其他行为。

回答by Alissa

setting isSpyto falseis a very bad idea, since then you spy on a spy and when Jasmine clears the spies at the end of your spec you won't get the original method. the method will be equal to the first spy.

设置isSpyfalse是一个非常糟糕的主意,因为那样你就监视了一个 spy,而当 Jasmine 在你的规范末尾清除了这些 spy 时,你将无法获得原始方法。该方法将等于第一个间谍。

if are already spying on a method and you want the original method to be called instead you should call andCallThrough()which will override the first spy behavior.

如果已经在监视某个方法并且您希望调用原始方法,则应该调用andCallThrough()它将覆盖第一个监视行为。

for example

例如

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

you can clear all spies by calling this.removeAllSpies()(this- spec)

您可以通过调用this.removeAllSpies()( this-spec)清除所有间谍

回答by uglymunky

I think that's what .reset()is for:

我认为这就是.reset()的用途:

spyOn($, 'ajax');

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

expect($.ajax).toHaveBeenCalled();

$.ajax.calls.reset()

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

回答by FilmJ

So spies are reset automatically between specs.

所以间谍会在规格之间自动重置。

You actually do not get the benefit of "restoration" of the original function if you use andCallFake()within a beforeEach()and then attempt to forcibly change it within a spec (which is likely why it tries to prevent you from doing so).

如果您andCallFake()在 a 中使用beforeEach()然后尝试在规范中强行更改它,您实际上不会获得“恢复”原始功能的好处(这可能是它试图阻止您这样做的原因)。

So be careful, especially if your spy is being set on a global object such as jQuery.

所以要小心,特别是如果你的间谍被设置在一个全局对象上,比如 jQuery。

Demonstration:

示范:

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!

回答by titusd

In Jasmine 2, the spy state is held in a SpyStrategy instance. You can get hold of this instance calling $.ajax.and. See the Jasmine source code on GitHub.

在 Jasmine 2 中,间谍状态保存在 SpyStrategy 实例中。你可以得到这个调用实例$.ajax.and。请参阅GitHub 上的 Jasmine 源代码

So, to set a different fake method, do this:

因此,要设置不同的假方法,请执行以下操作:

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

To reset to the original method, do this:

要重置为原始方法,请执行以下操作:

$.ajax.and.callThrough();

回答by user7054363

This worked for me in Jasmine 2.5 to allow re-setting of mock ajax.

这在 Jasmine 2.5 中对我有用,以允许重新设置模拟 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);

然后你可以多次调用它而不会出错。spyOnAjax(mock1); spyOnAjax(mock2);

回答by RafaCianci

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..

在这种情况下,您使用相同的 Spy,但只需更改它将返回的 var..

回答by Hamzeen Hameem

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

从 jasmine 2.5 开始,您可以使用此全局设置来更新测试用例中的间谍:

jasmine.getEnv().allowRespy(true);

回答by LHM

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:

我发布此答案是为了解决 OP @Tri-Vuong 代码中的评论——这是我访问此页面的主要原因:

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 isSpyto 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:

@Alissa正确调用它的时候,她解释了为什么这是一个坏主意,集isSpyfalse-有效地刺探导致不再工作按计划茉莉的自动拆解行为间谍。她的解决方案(放置在 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
})

The last ittest 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.

最后一个it测试演示了如何将现有间谍的行为更改为原始行为之外的其他行为:“- anddeclare”之前存储在beforeEach(). 第一个测试说明了我这样做的用例 - 我希望间谍在大多数测试中以某种方式行事,但随后在一些测试中对其进行更改。

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

对于早期版本的茉莉花,改变相应的电话.andCallFake(.andCallThrough().andReturnValue(分别。

回答by dasAnderl ausMinga

just set the spy method to null

只需将 spy 方法设置为 null

mockedService.spiedMethod = null;