Использование Жасмин, чтобы шпионить за функцией без объекта
Я новичок в Жасмин и только начал использовать его. У меня есть файл библиотеки 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()