enhetstestning - Hur man stubbar importerat modul utan metoder i Angular?
Jag har en Angular-applikation med Jasmine-testningsramverk. Applikationen har en tjänst som heter AuthService som hanterar avkodning av JSON Web Tokens:
auth.service.ts
import * as jwtDecode from 'jwt-decode';
...
@Injectable()
export class AuthService {
...
public getTokenPayload(token) {
return jwtDecode(token);
}
}
Nu skulle jag vilja stubba modulen jwtDecode och returnera ett falskt värde för teständamål:
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');
});
Eftersom 'fake-token' inte är en giltig JSON Web Token, misslyckas mina tester med meddelandet:
InvalidTokenError: Invalid token specified: undefined is not an object (evaluating 'str.replace')
Detta är troligtvis ett fel som genereras från jwt-decode-modulen, vilket är förväntat. Jag vill inte behöva inkludera en annan modul bara för att skapa giltiga JSON Web Tokens för teständamål. Istället vill jag stubba funktionaliteten av jwtDecode().
Vad jag har försökt
1. Använda spyOn på jwtDecode
När jag använder spyOn behöver jag ett objekt med en metod. Så för den importerade jwtDecode fungerar detta inte, eftersom det är en funktion i sig:
spyOn(jwtDecode, '<method?>').and.callFake(() => 'fake-token');
2. Använda callFake på getTokenPayload
Jag har försökt använda:
spyOn(service, 'getTokenPayload').and.callFake(() => 'fake-token');
...och det förhindrar att några fel uppstår. Men min rapport om kodtäckning visar nu att funktionen getTokenPayload inte täcks. Dessutom har jag andra funktioner i applikationen som använder externa NPM-moduler och jag vill inte ignorera kodtäckning eftersom de kan ha andra implementeringar inuti metoden som bör testas.
3. Använda createSpy på jwtDecode
Jag försökte skriva över den importerade jwtDecode och skapa en spion:
const jwtDecode = jasmine.createSpy('jwtDecode').and.returnValue('fake-token');
Detta ger mig samma fel som ovan, vilket indikerar att jwtDecode inte är överskrivet inne i min faktiska AuthService-tjänst.
4. Använda window som objektet
Från denna fråga läste jag att globala moduler kan fästas vid window-objektet. Därför försökte jag göra samma sak för jwt-decode:
inne i testet...
console.log(window.jwtDecode); // undefined
console.log(window.jwt_decode); // undefined
Tyvärr är båda värdena undefined på window-objektet.
Fråga
Jag antar att frågan i allmänhet blir:
Hur stubbar man importerade NPM-moduler? Speciellt, hur stubbar man dem om de inte är ett objekt, utan en funktion (utan en metod att använda i Jasmine spyOn)?
Svar #1
Du är väldigt nära! Eftersom en tjänst bara är en klass, är det bästa sättet att testa den att instansiera en ny och spionera på den, vilket det verkar som att du gör. Om du vill spionera på den importerade metoden måste du på något sätt inkludera den i din tjänst. Annars finns det inget sätt för ditt test att veta vad den metoden är.
Så ha en egenskap på din tjänst:
jwtDecode = jwtDecode; // the imported one
Och kallade det som this.jwtDecode i din getTokenPayload-metod.
Då kommer följande att fungera:
const service = new AuthService( /* constructor args */ );
const jwtDecode = spyOn(service, 'jwtDecode');
jwtDecode.and.returnValue('fake-token');
Svar #2
Spionera helt enkelt på default exporten från jwt-decode-modulen, vilket är jwtDecode-funktionen. Detta sparar dig från att behöva förorena din tjänst som föreslaget.
Som ett exempel, i din spec-fil:
import * as jwt from 'jwt-decode';
...
// return whatever data structure you require
spyOn(jwt, 'default').and.returnValue({ someData: ['x', 'y', 'z'] });
