yial2
yial2

Reputation: 223

Angular 8 Jasmine/Karma spyOn behavior questions

I have a directive with custom debounce decorators. The directive is a fairly simple that listen on "scroll" event.

export class MyScrollDirective {
    @Output() scrolled = new EventEmitter<any>();

    @HostListener('scroll', ['$event'])
    //debouce is added to prevent multiple call when scroll reach the end
    //in the if statement below
    @myDebounce(300)
    onScroll(event) {
      this.scrolled.emit(null);
    }
}

In my test, I have an assumption that this.scrolled.emit() will be called whenever onScroll is called. Obviously this is not the case.

With the debounce, it looks like onScroll can be called multiple times from spyOn while this.scrolled only emit once. For example, if I trigger scroll events 2 times in my test within 300ms intervals, the onScroll is called 2 time while the this.scrolled has only 1 emit. The 1 emit is of course desired behavior, but how come onScroll is called twice. When the app is actually running in browser - instead of tests - the onScroll is actually called only once.

Why is this?!

The test detail can be view at StackBliz.io

Upvotes: 1

Views: 150

Answers (1)

Andrei Gătej
Andrei Gătej

Reputation: 11979

This is a very interesting question!

In order to get a better understanding of how things work, I'd recommend opening this forked StackBlitz and place these breakpoints:

  • jasmine.js
    • calls.push(context); - line 2162
    • { - line 5972
  • myDebounceDecorator.ts
    • descriptor.value - line 16
    • var params = []; - line 18
  • dummy.component.spec.ts
    • vsCss.triggerEventHandler(...) - line 36
    • vsCss.triggerEventHandler(...) - line 40

Note: I've used Firefox Dev Tools

This is what happens after you refresh the app:

  • descriptor.value; is reached; this means that the spy will be applied on the newly created function:
descriptor.value = function () { /* this will be spied */ }
  • vsCss.triggerEventHandle (line 36) is reached

  • var callData = { is reached, because of the spy.

  • calls.push(context); is reached, which means the spy(onScroll) was called

  • var params = []; is reached, because the spy was defined with callThrough(), which means that the original implementation will be used

  • vsCss.triggerEventHandler (line 40) is reached

  • var callData = { is reached, because of the spy

  • calls.push(context); is reached again; if we were to hover over calls, we'd see that it already has an element(from the previous vsCss.triggerEventHandle), hence the calls' length will be 2

  • var params = []; - the original implementation is used

  • var callData = { is reached; this time, it's the spy what we used on the Subject(i.e EventEmitter); we can be sure of this by hovering over this from object: this

  • calls.push(context); - calls belongs to the Subject's spied method

  • var callData = { is reached; this time, the spy belongs to onDummy method

  • calls.push(context); - onDummy's spy

Upvotes: 1

Related Questions