Попытка шпионить (Jasmine) на методы Array.prototype вызывает переполнение стека
Это довольно странно. С использованием testem
бегун с jasmine2
и выполняется следующая спецификация (хотя она правильно отмечает, что ожиданий нет):
describe('Spying on array.prototype methods', function(){
it('should work this way', function(){
spyOn( Array.prototype, 'push' ).and.callThrough();
// expect(1).toBe(1);
});
});
Тем не менее, добавить expect
(любой expect
!) и это приводит к переполнению стека следующим сообщением в testem
приставка: RangeError: Maximum call stack size exceeded. at http://localhost:7357/testem/jasmine2.js, line 980
HTML-страница отчета попадает в спецификацию, а затем зависает, не показывая никаких реальных результатов.
В конечном итоге я хотел бы сделать что-то вроде этого:
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();
});
});
Заранее спасибо всем, кто может пролить свет на эту странность. Могу ли я не шпионить за родными прототипами?
1 ответ
Когда вы шпионите за чем-то, Жасмин создает порядок оболочки, чтобы отслеживать вызов этой функции. Здесь, когда вы шпионите за методом-прототипом, в принципе, даже операция push в самом jasmine вызывает 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 этого экземпляра массива (или, другими словами, ссылки (в настоящее время унаследованной от прототипа Array), хранящейся в push
этого экземпляра перезаписывается новой ссылкой на функцию шпиона, созданного jasmine): пример:
it('does not use native push', function(){
var arr = [];
spyOn(arr, 'push' ).and.callThrough();
arr.push(1);
expect(arr.push).toHaveBeenCalledWith(1);
});
Но как реальный вариант использования (по крайней мере, мне никогда не приходилось), вы всегда можете проверить длину целевого массива и получить последний элемент для сравнения после определенной операции. Вам, вероятно, никогда не понадобится шпионить за нативными методами (по крайней мере, не за массивом:)), а вместо этого протестировать объект, который вас интересует, и следить за этими целевыми методами.