使用Jasmine监视没有对象的函数

Using Jasmine to spy on a function without an object

本文关键字:对象 函数 Jasmine 监视 使用      更新时间:2023-09-26

我正在使用Jasmine,并且有一个库js文件,其中包含许多与任何对象无关的功能(即全局(。如何监视这些功能?

我尝试使用窗口/文档作为对象,但即使调用了该函数,间谍也不起作用。我还尝试将其包装在假物体中,如下所示:

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

并测试

expect(fakeElement.fakeMethod).toHaveBeenCalled();

这也不起作用,因为间谍不起作用。

如果你正在定义你的函数:

function test() {};

那么,这相当于:

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

所以spyOn(window, 'test')应该有效。

如果不是这样,您还应该能够:

test = jasmine.createSpy();

如果这些都不起作用,则您的设置正在发生其他事情。

我不认为你的fakeElement技术有效,因为幕后发生的事情。原始的 globalMethod 仍然指向相同的代码。间谍所做的是代理它,但仅限于对象的上下文中。如果你能让你的测试代码通过fakeElement调用,它将起作用,但这样你就可以放弃全局fns。

TypeScript 用户:

我知道OP询问了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 () 编写 Jasmine 测试时,您可以按如下方式监视 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();
});

我的回答与@FlavorScape略有不同,因为我在导入的模块中有一个(默认导出(函数,我做了以下操作:

import * as functionToTest from 'whatever-lib';
const fooSpy = spyOn(functionToTest, 'default');

我找到了一种新的方法,因为建议的解决方案对我不起作用:(所以你可以这样做:

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';
}

我想这是最简单的方法:

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