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();
});