I have a component that updates a variable on specific router events. How do I unit test to make sure the code below works correctly?
router.events.subscribe(event => {
if (event instanceof NavigationStart) {
this.isLoading = true;
} else if (event instanceof NavigationEnd || event instanceof NavigationCancel) {
this.isLoading = false;
} else if (event instanceof NavigationError) {
this.isLoading = false;
// console.error('effect error: ', error);
}
});
Router stub
class MockRouter {
navigate = jasmine.createSpy('navigate');
start = new NavigationStart(0, '/home');
end = new NavigationEnd(1, '/home', '/dashboard');
events = new Observable(observer => {
observer.next(this.start);
observer.next(this.end);
observer.complete();
});
}
We can test routing in Angular by using RouterTestingModule instead of RouterModule to provide our routes. This uses a spy implementation of Location which doesn't trigger a request for a new URL but does let us know the target URL which we can use in our test specs.
In order to detect route change at any moment in AngularJS, this can be achieved by using the $on() method.
You need to create a simple Router stub to allow emitting various router events during the test.
Router stub:
// mocked source of events
const routerEventsSubject = new Subject<RouterEvent>();
const routerStub = {
events: routerEventsSubject.asObservable()
};
describe('FooComponent', () => {
...
Then you simply use that stub:
...
let router: Router;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
{
provide: Router,
useValue: routerStub
}
]
});
router = TestBed.inject(Router);
...
A test looks like this:
it('should be loading on a navigation start', () => {
// create a navigation event
routerEventsSubject.next(new NavigationStart(1, 'start'));
expect(component.isLoading).toBeTruthy();
});
I was in a similar situation, and this is how I figured it out:
describe('router.events', () => {
const mockStart = of(new NavigationStart(0, '/testUrl'));
const mockEnd = of(new NavigationEnd(0, '/testUrl', '/testUrlRedirect'));
const mockCancel = of(new NavigationCancel(0, '/testUrl', '/testReason'));
const mockError = of(new NavigationError(0, '/testUrl', 'test error'));
let routerEventSpy: jasmine.Spy;
beforeEach(() => {
routerEventSpy = spyOn(component.router, 'events');
});
it('should set isLoading to true', () => {
// Arrange
routerEventSpy.and.returnValue(mockStart);
component.isLoading = false; // initial value to make sure code is effective
// Act
// Call whatever method contains your router.events.subscribe - for example:
component.ngOnInit(); // <-- Just an example, you should replace this with your corresponding method
// Assert
expect(component.isLoading).toBe(true);
});
it('should set isLoading to false for NavigationEnd', () => {
// Arrange
routerEventSpy.and.returnValue(mockEnd);
component.isLoading = true; // initial value, see first test
// Act
// Call whatever method contains your router.events.subscribe - for example:
component.ngOnInit(); // <-- Just an example, see first test
// Assert
expect(component.isLoading).toBe(false);
});
it('should set isLoading to false for NavigationCancel', () => {
// Arrange
routerEventSpy.and.returnValue(mockCancel);
component.isLoading = true; // initial value, see first test
// Act
// Call whatever method contains your router.events.subscribe - for example:
component.ngOnInit(); // <-- Just an example, see first test
// Assert
expect(component.isLoading).toBe(false);
});
it('should set isLoading to false for NavigationError', () => {
// Arrange
routerEventSpy.and.returnValue(mockError);
component.isLoading = true; // initial value, see first test
// Act
// Call whatever method contains your router.events.subscribe - for example:
component.ngOnInit(); // <-- Just an example, see first test
// Assert
expect(component.isLoading).toBe(false);
});
});
A few notes:
component
should be replaced with whatever you store the fixture.componentInstance
in. It is the this
in your tests.Observable
and its methods that you have declared - the spy
is returning of
observables to trigger the subscribe
. There's likely a way to use that Router Stub for your purposes (e.g. @Yuri's answer), but your question only asked for a way to test that logic and the isLoading
variable.protected
or private
(a good practice), you can still spy on it with bracket notation - e.g. spyOn(component['router'], 'events')
.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With