Reputation: 2860
I am having an issue in an Angular2 text component where I am getting the following error when I attempt to run the testrunner:
Component: Product Component Should ensure component subscribes to service EventEmitter on instantiation
Failed: Cannot read property 'detectChanges' of undefined
TypeError: Cannot read property 'detectChanges' of undefined
Component: Product Component Should check that service getProducts is called when component getProducts is called
Failed: Cannot read property 'getProducts' of undefined
TypeError: Cannot read property 'getProducts' of undefined
Here is my test module:
import { ProductService } from "../../../../services/product.service";
import { TestBed, ComponentFixture, async } from "@angular/core/testing";
import { ProductComponent } from "../../../../components/catalog/products/ProductComponent";
import { HttpModule } from "@angular/http";
import { DebugElement, CUSTOM_ELEMENTS_SCHEMA } from "@angular/core";
import { Observable } from "rxjs/Rx";
class MockProductService {
emitter = Observable.of({});
constructor() {
}
getProducts() {
return this;
}
}
let comp: ProductComponent;
let fixture: ComponentFixture<ProductComponent>;
let de: DebugElement;
let el: HTMLElement;
describe('Component: Product Component', () => {
let mockProductService;
beforeEach(() => {
mockProductService = new MockProductService();
TestBed.configureTestingModule({
declarations: [
ProductComponent
],
providers: [
{
provide: ProductService, useValue: mockProductService
}
],
imports: [
HttpModule
],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
.compileComponents()
.then(() => {
fixture = TestBed.createComponent(ProductComponent);
comp = fixture.componentInstance;
});
});
it('Should ensure component subscribes to service EventEmitter on instantiation', () => {
// TestBed.compileComponents()
//.then(() => {
//spyOn(mockProductService, 'emitter').and.returnValue({
// subscribe: () => {}
//});
fixture.detectChanges();
fixture.whenStable().then(() => {
expect(comp.products).toBe({});
});
//expect(mockProductService.emitter).toHaveBeenCalled();
//});
});
it('Should check that service getProducts is called when component getProducts is called', () => {
//TestBed.compileComponents()
// .then(() => {
spyOn(mockProductService, 'getProducts').and.returnValue({
subscribe: () => {}
});
comp.getProducts({});
expect(mockProductService.getProducts).toHaveBeenCalled();
// });
});
});
As the component under test uses an external template via the templateUrl property of the class I have to use the compileComponents method of the TestBed to compile this template ready for the test. As you can see this retyrns a promise which inside the call back I then define the fixture and the component instance ready for each test. As this is in the beforeEach this will be done for every test.
When I attempt to access the fixture and component instances from within my tests it is saying they are undefined which leads me to believe that the tests are being called before the promise is being returned from the compileComponents method. I have tried moving the TestBed.CompileComponents to within each test spec but this causes an error too.
Can anyone advise as to what I need to change here? Thanks
Upvotes: 1
Views: 3184
Reputation: 122116
I've used both of the following methods successfully. Either use DoneFn
to hold off on the beforeEach
until everything's finished:
beforeEach(done => {
mockProductService = new MockProductService();
TestBed.configureTestingModule({
...
})
.compileComponents()
.then(() => {
fixture = TestBed.createComponent(ProductComponent);
comp = fixture.componentInstance;
done();
});
});
Or use async
and split the beforeEach
into two parts:
beforeEach(async(() => {
mockProductService = new MockProductService();
TestBed.configureTestingModule({
...
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ProductComponent);
comp = fixture.componentInstance;
});
Upvotes: 2