Использование Жасмин, чтобы шпионить за функцией без объекта

Я новичок в Жасмин и только начал использовать его. У меня есть файл библиотеки JS с большим количеством функций, которые не связаны ни с одним объектом (то есть являются глобальными). Как мне следить за этими функциями?

Я пытался использовать окно / документ в качестве объекта, но шпион не работал, хотя функция была вызвана. Я также попытался обернуть его в поддельный объект следующим образом:

var fakeElement = {};
fakeElement.fakeMethod = myFunctionName;
spyOn(fakeElement, "fakeMethod");

и проверить с

expect(fakeElement.fakeMethod).toHaveBeenCalled();

Это не работает, так как шпион не работал

11 ответов

Решение

Если вы определяете свою функцию:

function test() {};

Тогда это эквивалентно:

window.test = function() {}  /* (in the browser) */

Так spyOn(window, 'test') должно сработать.

Если это не так, вы также должны быть в состоянии:

test = jasmine.createSpy();

Если ни один из них не работает, что-то еще происходит с вашей настройкой.

Я не думаю, что ваш fakeElement Техника работает из-за того, что происходит за кулисами. Оригинальный globalMethod по-прежнему указывает на тот же код. Шпионаж - это прокси, но только в контексте объекта. Если вы можете заставить свой тестовый код вызывать через fakeElement, он будет работать, но тогда вы сможете отказаться от глобальных fns.

Пользователи TypeScript:

Я знаю, что ОП спрашивал о javascript, но для всех пользователей TypeScript, которые сталкиваются с этим и хотят следить за импортированной функцией, вот что вы можете сделать.

В тестовом файле преобразуйте импорт функции из этого:

import {foo} from '../foo_functions';

x = foo(y);

К этому:

import * as FooFunctions from '../foo_functions';

x = FooFunctions.foo(y);

Тогда вы можете шпионить за FooFunctions.foo:)

spyOn(FooFunctions, 'foo').and.callFake(...);
// ...
expect(FooFunctions.foo).toHaveBeenCalled();

Есть 2 варианта, которые я использую (для жасмина 2)

Это не совсем ясно, потому что кажется, что функция на самом деле является подделкой.

test = createSpy().and.callFake(test); 

Второй более многословный, более явный и "чище":

test = createSpy('testSpy', test).and.callThrough();

-> Исходный код жасмина, чтобы увидеть второй аргумент

Очень простой способ:

import * as myFunctionContainer from 'whatever-lib';

const fooSpy = spyOn(myFunctionContainer, 'myFunc');

Решение с помощью TypeScript

Замените свойimport:

      import { yourGlobalFunction } from '../your-global-functions';

с:

      import * as Functions from '../your-global-functions';

Затем:

      spyOnProperty(Functions, 'yourGlobalFunction').and.returnValue(() => 'mock return value');
import * as saveAsFunctions from 'file-saver';
..........
....... 
let saveAs;
            beforeEach(() => {
                saveAs = jasmine.createSpy('saveAs');
            })
            it('should generate the excel on sample request details page', () => {
                spyOn(saveAsFunctions, 'saveAs').and.callFake(saveAs);
                expect(saveAsFunctions.saveAs).toHaveBeenCalled();
            })

Это сработало для меня.

Обычно мы придерживаемся следующего подхода:

utils.ts файл для всех глобальных утилит:

      function globalUtil() {
  // some code
}

abc.component.ts:

      function foo {
  // some code
  globalUtil();  // calls global function from util.ts
}

При написании теста Жасмина на function foo (), вы можете следить за функцией globalUtil следующим образом:

abc.component.spec.ts:

      import * as SharedUtilities from 'util.ts';

it('foo', () =>
{
  const globalUtilSpy = jasmine.createSpy('globalUtilSpy');
  spyOnProperty(SharedUtilities, "globalUtilSpy").and.returnValue(globalUtilSpy);
  
  foo();
  expect(globalUtilSpy).toHaveBeenCalled();
});

Я нашел новый способ, потому что предложенные решения у меня не работают :( Итак, вы можете сделать это так:

      import * as FooFunctions from 'foo-functions';

spyOnProperty(FooFunctions, 'foo').and.returnValue(jasmine.createSpy());

Если хочешь, сделайcallThrough:

      import * as FooFunctions from 'foo-functions';

const originalFn = FooFunctions.foo;
spyOnProperty(FooFunctions, 'foo').and.returnValue(
    jasmine.createSpy().and.callFake(originalFn)
);

Чтобы было удобнее, я сделал помощника. Вы можете использовать его следующим образом:

      import * as FooFunctions from 'foo-functions';

spyOnFunction(FooFunctions, 'foo'); // to call through
spyOnFunction(FooFunctions, 'foo').and.callFake(...) // to call fake
spyOnFunction(FooFunctions, 'foo').and... // to do something else

Вот вспомогательный код:

      function spyOnFunction<T, K extends keyof T>(source: T, originalFnKey: K): jasmine.Spy {
    const originalFn: T[K] = source[originalFnKey];

    if (!isFunction(originalFn)) {
        throw new Error('[spyOnFunction] spy target must be a function');
    }

    const spy: jasmine.Spy = jasmine.createSpy().and.callFake(originalFn);

    spyOnProperty(source, originalFnKey).and.returnValue(spy);

    return spy;
}

function isFunction(item: unknown): item is (...args: unknown[]) => unknown {
    return typeof item === 'function';
}

Мой ответ немного отличается от @FlavorScape тем, что у меня была одна функция (экспорт по умолчанию) в импортированном модуле, я сделал следующее:

import * as functionToTest from 'whatever-lib';

const fooSpy = spyOn(functionToTest, 'default');

Я думаю, это самый простой способ:

      const funcSpy = spyOn(myFunc, 'call');

Это должно быть возможно без определения окна. глобальная функция. Пытаться:

   var x = function() {}

   spyX = spyOn(x, 'call') //

   // something, that calls x
   expect(spyX).toBeCalled()
Другие вопросы по тегам