Angular 2: How to mock ChangeDetectorRef while unit testing
Update 2020:
I wrote this originally in May 2017, it's a solution that worked great at the time and still works.
We can't configure the injection of a changeDetectorRef mock through the test bed, so this is what I am doing these days:
it('detects changes', () => { // This is a unique instance here, brand new const changeDetectorRef = fixture.debugElement.injector.get(ChangeDetectorRef); // So, I am spying directly on the prototype. const detectChangesSpy = spyOn(changeDetectorRef.constructor.prototype, 'detectChanges'); component.someMethod(); // Which internally calls the detectChanges. expect(detectChangesSpy).toHaveBeenCalled(); });
Then you don't care about private attributes or any.
In case anyone runs into this, this is one way that has worked well for me:
As you are injecting the ChangeDetectorRef instance in your constructor:
constructor(private cdRef: ChangeDetectorRef) { }
You have that cdRef
as one of the private attributes on the component, which means you can spy on the component, stub that attribute and have it return whatever you want. Also, you can assert its calls and parameters, as needed.
In your spec file, call your TestBed without providing the ChangeDetectorRef as it won't provide what you give it. Set the component that same beforeEach block, so it is reset between specs as it is done in the docs here:
component = fixture.componentInstance;
Then in the tests, spy directly on the attribute
describe('someMethod()', () => { it('calls detect changes', () => { const spy = spyOn((component as any).cdRef, 'detectChanges'); component.someMethod(); expect(spy).toHaveBeenCalled(); });});
With the spy you can use .and.returnValue()
and have it return whatever you need.
Notice that (component as any)
is used as cdRef
is a private attribute. But private doesn't exist in the actual compiled javascript so it is accessible.
It is up to you if you want to access private attributes at runtime that way for your tests.
Not sure if this a new thing or not, but changeDetectorRef can be accessed via fixture.
See docs: https://angular.io/guide/testing#componentfixture-properties
We ran into the same issue with change detector mocking and this is ended up being the solution
For unit testing, if you are mocking ChangeDetectorRef
just to satisfy dependency injection for a component to be creation, you can pass in any value.
For my case, I did this:
TestBed.configureTestingModule({ providers: [ FormBuilder, MyComponent, { provide: ChangeDetectorRef, useValue: {} } ]}).compileComponents()injector = getTestBed()myComponent = injector.get(MyComponent)
It will create myComponent
successfully. Just make sure test execution path does not need ChangeDetectorRef
. If you do, then replace useValue: {}
with a proper mock object.
In my case, I just needed to test some form creation stuff using FormBuilder
.