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


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 2015年

Answers:


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 2012年

1
这不是一个好主意,请参阅我的回答。
FilmJ 2013年

3
这确实不是一个好主意,这样在规范运行完成之后,该方法将仍然是间谍,而不是原始方法,请参见Alissa的回答
Leon Fedotov

提示:在当前版本的Jasmine中,您需要使用“ and.callFake(...)”而不是“ andCallFake(...)”
Ilker Cat,

7
这不应该是公认的答案!请参见@Alissa的答案
克里斯·奈特

132

设置isSpyfalse是一个非常糟糕的主意,因为那么你窥视一个间谍,当茉莉花在你规范结尾清除间谍,你不会得到原来的方法。该方法将等于第一个间谍。

如果已经在监视某个方法,而您想调用原始方法,则应该调用andCallThrough()它将覆盖第一个间谍行为的方法。

例如

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

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


1
这是恕我直言的最佳解决方案。
Matthieu Riegler 2014年

我更喜欢说将isSpy设置为false是一件不好的事情。我喜欢Andreas K的开箱即用思维。
KSev 2014年

22
请注意,在茉莉2中,它是spyObj.and.callThrough();。
鲍里斯·夏彭迪耶

4
如果您不想调用原始函数,而只是为嵌套描述覆盖原始间谍,则可以使用“ spyObj.and.returnValue('some other value');”返回另一个值来实现。
Ilker Cat

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

18
请注意,这种情况已经改变,以mySpy.calls.reset()在茉莉花2
亨里克ñ

1
mySpy.calls.reset()重置间谍被调用时间的计数器。您可以通过expect(spy).toHaveBeenCalledTimes(1)
Spiral Out

1
完全错误地回答问题的答案如何获得近40个投票?该问题询问如何覆盖andCallFake,而不是如何重置呼叫计数器。
Thor84no

20

因此,间谍会在规格之间自动重置。

如果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!

我试图找出我需要做的事情以停止监视某个物体。您的答案非常明确且很有帮助。茉莉花岩石可自动卸除。
xverges 2013年

9

在Jasmine 2中,间谍状态保留在SpyStrategy实例中。您可以掌握此实例的调用$.ajax.and。请参阅GitHub上的Jasmine源代码

因此,要设置其他伪造方法,请执行以下操作:

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

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

$.ajax.and.callThrough();

2
我认为$.ajax.and.andCallThrough();不正确。应该是$.ajax.and.callThrough();
kriskodzi

那对我有用;里面beforeEachspyOn(Foobar, 'getFoo').and.returnValue('generic'); }然后里面itFoobar.getFoo.and.returnValue('special')。谢谢!
jakub.g

这是正确的答案。我做同样的事情。我从这篇文章中得到了这个主意:github.com/jasmine/jasmine/issues/160。我不明白为什么这不是最佳答案?
c_breeez

7

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

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


2

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

})

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


1

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

我想覆盖间谍...在这里做一些不同的事情

到目前为止,没有一个答案可以解决这一点,因此,我将发布我所学的知识并总结其他答案。

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

最后一个it测试演示了如何将现有间谍的行为更改为除原始行为之外的其他行为:“ and-declare”先前存储在变量中的spyObj的新行为beforeEach()。第一个测试说明了执行此操作的用例-我希望间谍在大多数测试中表现出一定的行为,然后在以后的一些测试中对其进行更改。

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


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.