0

I'm new to angular unit testing and want to test my services file. How can I do that?.

Tried testing the void methods but still confuse where to start. How can I use stub() to mock data or if there's any other way to do it?. Here's my code.

import { Injectable, Inject } from '@angular/core';
import { AUTH_API_URL, AuthenticationService } from 'ngx-login- 
client';
import { Router } from '@angular/router';
import { Broadcaster } from 'ngx-base';

@Injectable({
  providedIn: 'root'
})
export class LoginService {

static readonly REDIRECT_URL_KEY = 'redirectUrl';
static readonly DEFAULT_URL = '/_home';
static readonly LOGIN_URL = '/login';

constructor(
  private authService: AuthenticationService,
  private broadcaster: Broadcaster,
  private router: Router,
  @Inject(AUTH_API_URL) private authApiUrl: string
  ) {
  this.broadcaster.on('authenticationError').subscribe(() => {
    this.logout();
  });
}

redirectToAuth(): void {
  const redirectUrl = encodeURIComponent(window.location.href);
  const loginUrl = `${this.authApiUrl}login?redirect=${redirectUrl}`;
  window.location.href = loginUrl;
}

redirectAfterLogin(): void {
  const url = this.redirectUrl;
  this.router.navigateByUrl(url);
}

redirectToLogin(currentUrl: string): void {
  this.redirectUrl = currentUrl;
  window.location.href = LoginService.LOGIN_URL;
}

login(): void {
  const query = window.location.search.substr(1);
  const result: any = {};
  query.split('&').forEach(function(part) {
    const item: any = part.split('=');
    result[item[0]] = decodeURIComponent(item[1]);
  });

  if (result['error']) {
    console.log(result['error']);
  }

  if (result['token_json']) {
    // Handle the case that this is a login
    this.authService.logIn(result['token_json']);
    console.log('token is :' + result);
    // Navigate back to the current URL to clear up the query string
    // this.router.navigateByUrl(this.router.url);
  } else if (this.authService.isLoggedIn()) {
    // Handle the case the user is already logged in
    this.authService.onLogIn();
  }
}

logout(): void {
  this.authService.logout();
  window.location.href = '/';
}

set redirectUrl(value: string) {
  if (value) {
    localStorage.setItem(LoginService.REDIRECT_URL_KEY, value);
  }
}

get redirectUrl(): string {
  const res = localStorage.getItem(LoginService.REDIRECT_URL_KEY);
  localStorage.removeItem(LoginService.REDIRECT_URL_KEY);
  return res;
  }
}

I don't know where to start from.If someone can help me in telling how to unit test any function that is given in this code,then I can get an idea of how to start.

dmcgrandle
  • 5,934
  • 1
  • 19
  • 38
Rajat Singh
  • 653
  • 6
  • 15
  • 29

1 Answers1

2

There are different ways to approach this. I put together a StackBlitz to show one way to do so. Here are the specs from that StackBlitz:

it('should be createable', () => {
    expect(service).toBeTruthy();
});

describe('Login method', () => {
    it('should call logIn when query params include a token', () => {
        spyOn(service, '_search').and.returnValue('&test1=test1&token_json=TOKEN');
        service.login();
        expect(authServiceSpy.logIn).toHaveBeenCalled();
    });
    it('should call isLoggedIn and onLogIn when query params has no token', () => {
        spyOn(service, '_search').and.returnValue('&test1=test1');
        authServiceSpy.isLoggedIn.and.returnValue(true);
        service.login();
        expect(authServiceSpy.isLoggedIn).toHaveBeenCalled();
        expect(authServiceSpy.onLogIn).toHaveBeenCalled();
    });
});

A few notes needed to make this work:

  • I had to refactor the call to window.location.search because window.location is immutable and therefore cannot be spied on. Details here. All I did was a hack - I created a new LoginService class function called _search() which simply returns the call to window.location.search.substr(1). Note - this is NOT the right way to fix this. Instead I would suggest a more Angular approach using the Activated Route and queryParams. Details in the Official Docs.
  • I set up spies for the other dependency injections - AuthenticationService, Router, etc. The general pattern here is to put in spies so you can verify the functions were called, as well as return values as needed to test parts of your code
  • I did NOT thoroughly test your login() method. I simply showed an example of two paths through your logic - there are a number of other tests you will want to set up to achieve complete code coverage of this method if that is your goal.
  • For some reason StackBlitz complained that ngx-base and ngx-login-client weren't installed even though they were, so I simply mocked/stubbed those classes in other.services.ts. You will want to use the real thing in your code of course.

Best of luck, and I hope this helps give you an idea how to test Services in Angular.

dmcgrandle
  • 5,934
  • 1
  • 19
  • 38