1

I am trying to write a test case for one of the components in an application the constructor is as follows.

  constructor(private router: Router,
              public dialog: MatDialog,
              private tlsApiService: TlsApiService,
              private ncCutterSheetApiService: NcCutterSheetApiService,
              ) {
                this.outEvent = new EventEmitter<number>();

                router.events.forEach((event) => {
                  if(event instanceof NavigationStart) {
                    if(this.dialogRef){
                      this.dialogRef.close()
                    }
                  }
                  
                  // NavigationEnd
                  // NavigationCancel
                  // NavigationError
                  // RoutesRecognized
                });
              }

when I try to write a mock of the router using jasmine.createSpyObj I am getting this message.

TypeError: router.events.forEach is not a function

I have tried the following ways of creating the spyObject

    mockRouter = jasmine.createSpyObj(['events',['forEach']]);

    mockRouter = jasmine.createSpyObj('events',['forEach']);

    mockRouter = jasmine.createSpyObj(['events','forEach']);

I keep getting the same error. I have done google searches trying to find what I need and so far I have not found what I am looking for to get this to work. Any suggestions will be welcomed.

Eric Lommatsch
  • 129
  • 1
  • 13

2 Answers2

1

To create a spy for the events object on the router you need to first create an object, and create your spy on that object:

const mockRouter = { events: jasmine.createSpyObj('events', ['forEach']) };

How ever, best would be to rewrite your code like this:

router.events.pipe(
  filter((event): event is NavigationStart => event instanceof NavigationStart)
).subscribe((start) => {
  this.dialogRef?.close()
});

which you can then listen to pipe:

const mockRouter = { events: jasmine.createSpyObj('events', ['pipe']) };

However, perhaps it's more in place to listen to the subscribe :) but that's up to you

Poul Kruijt
  • 69,713
  • 12
  • 145
  • 149
  • @Akhil you are absolutely right, `forEach` is something legacy. I never use it anymore, especially because it returns a `Promise` and you are mixing two things then. I'll update my answer a bit – Poul Kruijt Jul 09 '20 at 19:52
  • Thanks for the confirmation. Appreciate the different approaches you've provided in your answer.... Upvoted – Akhil Jul 10 '20 at 19:52
  • If I was the only person working on this application re-writing to get rid of the forEach would be easy and I would have done so. I am working with a team and this is not the only place where a forEach is used. – Eric Lommatsch Jul 14 '20 at 17:21
0

First of all, I would suggest to rewrite your code in your TS with router.events.subsribe() instead of router.events.forEach().

The detailed reason can be found here

If you are willing to rewrite, then please go ahead and modify your TS like ::

router.events.subscribe(event => {
 if(event instanceof NavigationStart) {
 ...
 }

 if(event instanceof NavigationEnd) {
 ...
 }

);

and, in your spec.ts ::

class MockRouter {
  public ne = new NavigationEnd(0, '_url', '_url_after_redirects');

  public ns = new NavigationStart(0, 'url');

  public events = new Observable(observer => {
    observer.next(this.ne);
    observer.next(this.ns);
    observer.complete();
  });
}

Finally,

providers: [{provide: Router, useClass: MockRouter]
Akhil
  • 1,403
  • 7
  • 19