7

I'm trying to test that a component calls detectChanges on it's injected ChangeDetectorRef

I've stepped through the code and it's definitely called, but it appears I'm getting different values for the ChangeDetectorRef in the component and in the test. Here's the code. I've also tried the commented out spy, but it didn't work either.

it('should call ChangeDetectorRef.detectChanges from onFixedUpdate', () => {
    let changeDetectorService = fixture.debugElement.injector.get(ChangeDetectorRef, null);
    let spy = spyOn(changeDetectorService, 'detectChanges').and.callThrough();
    //let spy = spyOn(fixture.changeDetectorRef, 'detectChanges').and.callThrough();

    expect(spy).not.toHaveBeenCalled();

    fixture.componentInstance.onFixedUpdate(1);

    expect(spy).toHaveBeenCalled();
});
majinnaibu
  • 2,832
  • 1
  • 18
  • 22
  • I'm having this same problem. It's as if we don't have access to the instance of the ChangeDetectorRef service. – Felix Tsai Sep 05 '17 at 19:43
  • Does this answer your question? [Angular 2: How to mock ChangeDetectorRef while unit testing](https://stackoverflow.com/questions/41421807/angular-2-how-to-mock-changedetectorref-while-unit-testing) – Alex Parloti Jun 08 '20 at 08:50

3 Answers3

10

First some considerations:

  1. ChangeDetectorRef is not provided through DI, so you cannot provide a double.
  2. fixture.changeDetectorRef is not the same as the component provided, so you cannot use this.
  3. fixture.debugElement.injector.get(ChangeDetectorRef) will create a new instance of the private class ViewRef (the public class ViewRef is just an alias for ViewRef$1), every time it is invoked, then the object provided to the component is another.

So my solution is:

// Get a reference to an instance of the private class
const changeDetectorRef = fixture.debugElement.injector.get(ChangeDetectorRef);
// spy on private class prototype
const detectChangesSpy = spyOn(changeDetectorRef.constructor.prototype, 'detectChanges');
Alex Parloti
  • 668
  • 1
  • 9
  • 25
1

I had the same problem and used the technique described in Juan's answer to a similar SO question.

The basic gist is to spy directly on the property in your component to which the injected ChangeDetectorRef is assigned. If you have marked this property as private, which is often the case, there is a workaround by casting the component to any and then using that to create the spy on the private property.

Please note I would have simply added this as a comment but I do not have high enough rep - I hope this is ok!

TomP
  • 89
  • 4
1

Let's assume, inside your component, you've named the injected ChangeDetectorRef as cdr.

it('should call ChangeDetectorRef.detectChanges from onFixedUpdate', () => {

   // grab the the reference to the ChangeDetectorRef (cdr) directly from the component
   let spy = spyOn((<any>fixture.componentInstance).cdr, 'detectChanges').and.callThrough();

   expect(spy).not.toHaveBeenCalled();

   fixture.componentInstance.onFixedUpdate(1);

   expect(spy).toHaveBeenCalled();
});
altgov3en
  • 1,100
  • 2
  • 18
  • 33