Home > Mobile >  Jasmine Unit Test for to check activate route has two parameters and call service function
Jasmine Unit Test for to check activate route has two parameters and call service function

Time:07-20

I have a function in my component to get current route parameters and call a function in a service if two parameters are present.

Component.ts:

listenToRouteParameters(): void {
    const state = this.route.snapshot?.queryParamMap.get('state');
    const code = this.route.snapshot?.queryParamMap.get('code');
    if (state && code) {
      const codeVerifier = this.cookieService.getCookieValue(state);
      if (codeVerifier) {
        this.cookieService.removeCookie();
        this.initiateTokenExchange(code, codeVerifier);
      } else {
        this.refreshTokens();
      }
    } else {
      this.refreshTokens();
    }

}

I wrote a unit test for it as follows.

let component: LoginComponent;
  let fixture: ComponentFixture<LoginComponent>;
  let el: HTMLElement;
  let router: Router;
  let route: ActivatedRoute;
  let cookieService: CookieService;
  const paramsSubject = new BehaviorSubject({
    state: '323232323',
    code: '232323232',
  });

beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [ LoginComponent ],
      imports: [
        FormsModule,
        ReactiveFormsModule,
        HttpClientModule,
        RouterTestingModule
      ],
      providers:[
        {
          provide: ActivatedRoute,
          useValue: {
            params: paramsSubject
          },
        },
        { provide: CookieService, useValue: cookieService}
      ]
    })
    .compileComponents();
  });

  beforeEach(() => {
    fixture = TestBed.createComponent(LoginComponent);
    component = fixture.componentInstance;
    router = TestBed.get(Router)
    route = TestBed.get(ActivatedRoute)
  });

  it('should retrieve cookie if current route has state and code params', () => {
    const activatedRoute: ActivatedRoute = fixture.debugElement.injector.get(ActivatedRoute);

    activatedRoute.queryParams = of({ state: '123' });

    fixture.detectChanges(); 
    //  tick();

    activatedRoute.queryParams.subscribe((value) => {
      expect(cookieService.getCookieValue).toHaveBeenCalled();
    })
  });

This unit test pass regardless of passing parameters or not. Appreciate it if anyone can have a look and tell me how to properly write unit tests for the scenario.

Edit: CookieService.ts

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

  constructor() { }

  /**
   * Set cookie
   * @param state State value
   * @param codeVerifier Code verifier value
   */
  setCookie(state: string, codeVerifier: string): void {
    document.cookie = `app.txs.${state}=${codeVerifier};secure;sameSite=strict;`;
  }

  /**
   * Get cookie value
   * @param state 
   * @returns 
   */
  getCookieValue(state: string | null): string | undefined {
    return document.cookie.split('; ').find(row => row.startsWith(`app.txs.${state}=`))?.split('=')[1];
  }

  /**
   * Remove cookie
   */
  removeCookie(): void {
    let cookies = document.cookie.split(";");
    for (let i = 0; i < cookies.length; i  ){   
      let spcook =  cookies[i].split("=");
      document.cookie = spcook[0]   "=;expires=Thu, 21 Sep 1979 00:00:01 UTC;";                                
    }
  }
}

CodePudding user response:

You can do something like this, follow the comments with !!:

// !! change the declaration to this !!
let cookieService: jasmine.SpyObj<CookieService>;
beforeEach(async () => {
    // !! add this !!
    // !! the 2nd argument accepts an array of strings that will
    // mock the public methods as spies !!
    cookieService = jasmine.createSpyObj('CookieService', ['getCookieValue', 'removeCookie']);
    await TestBed.configureTestingModule({
      declarations: [ LoginComponent ],
      imports: [
        FormsModule,
        ReactiveFormsModule,
        HttpClientModule,
        RouterTestingModule
      ],
      providers:[
        {
          provide: ActivatedRoute,
          useValue: {
            params: paramsSubject,
            // mock snapshot as well
            snapshot: {
              queryParamMap: {
                 get: () => {}
              }
            }
          },
        },
        { provide: CookieService, useValue: cookieService}
      ]
    })
    .compileComponents();
  });

  
  it('should retrieve cookie if current route has state and code params', () => {
    // !! you don't need this line, you already have a handle on activatedRoute
    // with route = TestBed.get(ActivatedRoute)
    // const activatedRoute: ActivatedRoute = fixture.debugElement.injector.get(ActivatedRoute);

    spyOn(route.snapshot.queryParamMap, 'get').and.callFake(param => {
       // !! mock however you wish
       if (param === 'code') {
          return 1;
       } else if (param === 'state') {
          return 2;
       }
    });

    fixture.detectChanges(); 
    
    // !! if listenToRouteParameters is called in the ngOnInit
    // then you won't have to explicitly call it because the first fixture.detectChanges()
    // above calls ngOnInit
    component.listenToRouteParameters();
    
    // !! make your expectation
    expect(cookieService.getCookieValue).toHaveBeenCalled();
  });
  • Related