I have an Angular Application with Jasmine Testing Framework. The Application has a Service called AuthService
that handles decoding JSON Web Tokens:
auth.service.ts
import * as jwtDecode from 'jwt-decode';
...
@Injectable()
export class AuthService {
...
public getTokenPayload(token) {
return jwtDecode(token);
}
}
Now I would like to stub the module jwtDecode
and return a fake value for the purpose if testing:
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');
});
Because 'fake-token'
is not a valid JSON Web Token, my tests fail with the message:
InvalidTokenError: Invalid token specified: undefined is not an object (evaluating 'str.replace')
This is probably an error generated from the jwt-decode
module, which is expected. I do not want to have to include another module just to create valid JSON Web Tokens for testing purposes. Instead, I'd like to stub the functionality of jwtDecode()
.
spyOn
on jwtDecode
When I use spyOn
, I need an object with a method. So for the imported jwtDecode
this won't work, since it is a function itself:
spyOn(jwtDecode, '<method?>').and.callFake(() => 'fake-token');
callFake
on getTokenPayload
I've tried using:
spyOn(service, 'getTokenPayload').and.callFake(() => 'fake-token');
...and that prevents any errors from happening. However, my code coverage report now shows that the function getTokenPayload
is not covered. Moreover, I have other function in the application that use external NPM Modules and I don't want to ignore code coverage since they might have other implementations inside the method that should be tested.
createSpy
on jwtDecode
I tried overriding the imported jwtDecode
and create a spy:
const jwtDecode = jasmine.createSpy('jwtDecode').and.returnValue('fake-token');
This gives me the same error as above, indicating that jwtDecode
is not overridden inside my actual AuthService
Service.
window
as the ObjectFrom this question I read that global modules might be attached to the window
Object. Hence, I tried doing the same thing for jwt-decode
:
inside the test...
console.log(window.jwtDecode); // undefined
console.log(window.jwt_decode); // undefined
Unfortunately, both values are undefined
on the window
Object.
I guess in general the question becomes:
How to stub imported NPM modules? Especially, how to stub them if they are not an object, but a function (without a method to use in Jasmine spyOn
)?
You're very close! Since a service is just a class, the best way to test it is to instantiate a new one and spy on it, as it appears you are doing. If you want to spy on the imported method, you will need to somehow include it in your service. Otherwise, there is no way for your test to know what that method is.
So have a property on your service:
jwtDecode = jwtDecode; // the imported one
And called it as this.jwtDecode
in your getTokenPayload
method.
Then the following will work:
const service = new AuthService( /* constructor args */ );
const jwtDecode = spyOn(service, 'jwtDecode');
jwtDecode.and.returnValue('fake-token');
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With