I am using Spectator to write my Angular 8 tests and Jest to run them. I am new to frontend unit testing, so I may have overlooked something simple; any ideas are welcome.
I have the following method (in Typescript) that returns a boolean, based on whether the current URL matches a set of paths or not (excluding queryParams and fragments):
// custom-breadcrumb.component.ts
private blacklistedPaths: string[] = [''];
constructor(private router: Router) {
}
hideBreadcrumb(): boolean {
let primaryUrlSegmentGroup: UrlSegmentGroup = this.router.parseUrl(this.router.url).root.children['primary'];
if(primaryUrlSegmentGroup == null){
return true;
}
let urlPath = primaryUrlSegmentGroup.segments.map((segment: UrlSegment) => segment.path).join('/');
return this.blacklistedPaths.some((path: string) => path === urlPath);
}
and
// custom-breadcrumb.component.html
<xng-breadcrumb [hidden]="hideBreadcrumb()">
<ng-container *xngBreadcrumbItem="let breadcrumb">
...
</ng-container>
</xng-breadcrumb>
I now want to write tests with Spectator that will validate the boolean return value based on a couple of possible urls. In Java I would emulate the Router
with a mock object and perform something along the lines of:
when(mockObject.performMethod()).thenReturn(myReturnValue);
How do I create a mock for the Router
? And how do I define the return values for this.router.parseUrl(this.router.url).root.children['primary']
?
This is what I currently have:
// custom-breadcrumb.component.spec.ts
import {SpectatorRouting, createRoutingFactory} from '@ngneat/spectator/jest';
describe('CustomBreadcrumbComponent', () => {
let spectator: SpectatorRouting<CustomBreadcrumbComponent>;
const createComponent = createRoutingFactory({
component: CustomBreadcrumbComponent,
declarations: [
MockComponent(BreadcrumbComponent),
MockPipe(CapitalizePipe)
],
routes: [{path: ''}] // I don't think this works
});
beforeEach(() => spectator = createComponent());
it('hideBreadcrumb - hide on homepage', () => {
// TODO set url path to ''
expect(spectator.component.hideBreadcrumb()).toBeTruthy();
});
it('hideBreadcrumb - show on a page other than homepage', () => {
//TODO set url path to 'test' for example
expect(spectator.component.hideBreadcrumb()).toBeFalsy();
});
});
I know that createRoutingFactory
supplies a ActivatedRouteStub
out of the box, but I haven't been able to do anything meaningful with it.
PS: I added karma as a tag as it may have the same solution, but do correct me if I am wrong.
I was under the impression that spectator.router
was returning me a mock, while I had to use spectator.get<Router>(Router)
to get it. The other problem I had was that the hideBreadcrumb
method in the html template was being loaded at component creation while I did not have the chance yet to mock the Router
. This is how I solved it:
Set detectChanges
to false to prevent the html template and ngOnInit from being loaded when I create the spectator component like so:
let spectator: SpectatorRouting<CustomBreadcrumbComponent>;
const createComponent = createRoutingFactory({
detectChanges: false,
component: CustomBreadcrumbComponent,
declarations: [ ... ]
});
beforeEach(() => {
spectator = createComponent()
});
Now it won't call hideBreadcrumb()
yet, which allows for successful creation of the spectator.
My test is this:
it('hideBreadcrumb - hide on homepage', () => {
let routerMock = spectator.get<Router>(Router);
routerMock.parseUrl.andReturn({root: {children: {'primary': {segments: [{path: ''}]}}}});
spectator.detectChanges();
expect(spectator.component.hideBreadcrumb()).toBeTruthy();
});
I retrieve a mock from the spectator with spectator.get<Router>(Router)
and I mock the return value of the parseUrl
method. I now allow the html template and ngOnInit
progression by setting spectator.detectChanges()
.