6

This is pretty odd. Using the testem runner with jasmine2 and the following spec executes (though it correctly flags that there are no expectations):

describe('Spying on array.prototype methods', function(){
  it('should work this way', function(){
    spyOn( Array.prototype, 'push' ).and.callThrough();
    // expect(1).toBe(1);
  });
});

However, add an expect (any expect!) and it causes the stack to overflow with the following message in the testem console: RangeError: Maximum call stack size exceeded. at http://localhost:7357/testem/jasmine2.js, line 980 The html report page gets up to the spec and then hangs without showing any actual results.

Ultimately I'd like to do something like this:

describe('Some structure.method', function(){
  it('does not use native push', function(){
    spyOn( Array.prototype, 'push' ).and.callThrough();
    [].push(1); // actually more like `myStructure.myMethod('test')`
    expect( Array.prototype.push ).not.toHaveBeenCalled();
  });
});

Thanks in advance to anyone who can shed light on this oddity. Can I not spy on native prototypical methods?

4

1 回答 1

7

当您监视某些东西时,茉莉花会创建一个包装器以跟踪该函数的调用。在这里,当您监视原型方法时,基本上即使 jasmine 中的 push 操作本身也会调用 spy 而不是数组上的实际 push 方法,并且会导致无限循环。

当您调用[].push(1)它时,它实际上会调用如下跟踪器

   spy = function() {
    callTracker.track({ //<-- Calls tracker to track invocation
      object: this,
      args: Array.prototype.slice.apply(arguments)
    });

它依次调用调用跟踪器并将调用上下文推送到其内部跟踪器数组并进入递归循环,直到调用堆栈爆炸。

this.track = function(context) {
  calls.push(context); //Now this again calls the spy
};

相反,如果您监视数组实例上的方法,则不会遇到此问题,因为它为该数组实例的 push 属性创建了一个间谍包装器(或换句话说push,该实例持有的引用(当前从数组原型继承)被茉莉花创建的间谍的新函数引用覆盖):示例:

it('does not use native push', function(){
  var arr = [];
  spyOn(arr, 'push' ).and.callThrough();
  arr.push(1);
  expect(arr.push).toHaveBeenCalledWith(1);
});

但是作为一个真实的用例(至少我从来不需要),您总是可以检查目标数组的长度并在特定操作之后获取要比较的最后一项。您可能永远不需要监视本机方法(至少不是数组:)),而是针对您感兴趣的对象进行测试并监视这些目标方法。

于 2015-06-11T00:05:41.017 回答