如何在Angular中没有方法存根导入的模块?

时间:2018-01-27 17:47:46

标签: angular unit-testing typescript jasmine karma-jasmine

我有一个使用Jasmine测试框架的Angular应用程序。该应用程序有一个名为AuthService的服务,用于处理JSON Web令牌的解码:

auth.service.ts

import * as jwtDecode from 'jwt-decode';
...

@Injectable()
export class AuthService {
  ...
  public getTokenPayload(token) {
    return jwtDecode(token);
  }
}

现在我想将模块jwtDecode存根并在测试时返回一个伪值:

auth.service.spec.ts

...

it('should get a token payload', () => {
  const fakeToken = 'fake-token';

  spyOn(service, 'getTokenPayload').and.callThrough();
  const tokenPayload = service.getTokenPayload(fakeToken);

  expect(tokenPayload).toBe('fake-token');
});

由于'fake-token'不是有效的JSON Web令牌,因此我的测试失败并显示以下消息:

InvalidTokenError: Invalid token specified: undefined is not an object (evaluating 'str.replace')

这可能是jwt-decode模块生成的错误,这是预期的。我不想仅仅为了测试目的而包含另一个模块来创建有效的JSON Web令牌。相反,我想要保留jwtDecode()的功能。

我尝试了什么

1。在spyOn

上使用jwtDecode

当我使用spyOn时,我需要一个带方法的对象。因此,对于导入的jwtDecode,这不会起作用,因为它本身就是一个函数:

spyOn(jwtDecode, '<method?>').and.callFake(() => 'fake-token');

2。在callFake

上使用getTokenPayload

我尝试过使用:

spyOn(service, 'getTokenPayload').and.callFake(() => 'fake-token');

...这可以防止任何错误发生。但是,我的代码覆盖率报告现在显示函数getTokenPayload未涵盖。此外,我在应用程序中还有其他使用外部NPM模块的功能,我不想忽略代码覆盖,因为它们可能在应该测试的方法中有其他实现。

3。在createSpy

上使用jwtDecode

我尝试重写导入的jwtDecode并创建一个间谍:

const jwtDecode = jasmine.createSpy('jwtDecode').and.returnValue('fake-token');

这给了我与上面相同的错误,表示我的实际jwtDecode服务中未覆盖AuthService

4。使用window作为对象

this question我读到全局模块可能附加到window对象。因此,我尝试为jwt-decode做同样的事情:

测试中的

...

console.log(window.jwtDecode); // undefined
console.log(window.jwt_decode); // undefined

不幸的是,undefined对象上的两个值均为window

问题

我猜一般来说问题就变成了:

如何存根导入的NPM模块?特别是,如果它们不是一个对象,而是一个函数(没有在Jasmine spyOn中使用的方法),如何对它们进行存根?

2 个答案:

答案 0 :(得分:4)

你非常接近!由于服务只是一个类,因此测试它的最佳方法是实例化一个新的并监视它,就像你正在做的那样。 如果您想监视导入的方法,则需要以某种方式将其包含在您的服务中。否则,您的测试无法知道该方法是什么。

所以你的服务有一个属性:

jwtDecode = jwtDecode; // the imported one

并在this.jwtDecode方法中将其称为getTokenPayload

然后以下内容将起作用:

const service = new AuthService( /* constructor args */ );

const jwtDecode = spyOn(service, 'jwtDecode');
jwtDecode.and.returnValue('fake-token');

答案 1 :(得分:0)

只需监视 default 模块中的 jwt-decode 导出,即 jwtDecode 函数。这样就不必按照建议污染您的服务。

例如,在您的规范文件中:

import * as jwt from 'jwt-decode';
...
// return whatever data structure you require
spyOn(jwt, 'default').and.returnValue({ someData: ['x', 'y', 'z'] });