I am having trouble writing a testing module for an Angular2 component I have created. The component in question subscribes to another service's EventEmitter in it's ngOnInit method. The component is then subscribed to this EventEmitter and listens for changes. Here is the component in question:
import { Component } from "@angular/core";
import { Product } from "../../../classes/Product";
import { ProductService } from "../../../services/product.service";
import { ConfigObject } from "../../../ConfigObject";
import { productHelper } from "../../../helpers/productHelper";
@Component({
selector: 'product-component',
templateUrl: '/app/views/catalog/products/product-dashboard.html',
moduleId: module.id
})
export class ProductComponent {
globals = ConfigObject;
products: Product[] = [];
productsLoaded = false;
productPayload = {
order : 'asc',
order_by : 'title',
category_id : 0,
resize : true,
imgHeight : 200,
imgWidth : 200,
active : 1,
searchTerm : '',
manufacturer : null
};
constructor(
private _productService: ProductService
) {
}
getProducts(filters) {
this.productsLoaded = false;
this._productService.getProducts(filters)
.subscribe(
products => { this.products = productHelper.processImagesAndDownloads(products)},
() => { },
() => { this.productsLoaded = true }
);
}
ngOnInit() {
this._productService.emitter.subscribe(
(products) => {
this.products = productHelper.processImagesAndDownloads(products);
},
() => { },
() => { }
);
this.getProducts({});
}
}
As you can see, using the ngOnInit method the _productService.emitter EventEmitter is subscribed to.
I have attempted to use the spyOn method to mock this event emitter but with no success. I cannot seem to get this component testing correctly. Can anyone see what the problem is here:
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";
let MockProductService = {
emitter: () => {}
};
let comp: ProductComponent;
let fixture: ComponentFixture<ProductComponent>;
let de: DebugElement;
let el: HTMLElement;
describe('Component: Product Component', () => {
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [
ProductComponent
],
providers: [
{
provide: ProductService, useValue: MockProductService
}
],
imports: [
HttpModule
],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
});
});
it('Should check that products are loaded in the template', async(() => {
TestBed.compileComponents()
.then(() => {
fixture = TestBed.createComponent(ProductComponent);
comp = fixture.componentInstance;
spyOn(MockProductService, 'emitter').and.returnValue({
subscribe: () => {}
});
comp.ngOnInit();
expect(MockProductService.emitter).toHaveBeenCalled();
});
}));
});
The error I receive is:
Failed: Uncaught (in promise): TypeError: this._productService.emitter.subscribe is not a function
emitter
doesn't get called as a method from the component. It is only accessed as a property
this._productService.emitter
And because it never gets called as a method, your spy is useless.
You could just assign the value of the emitter
to an Observable
. That way, when the component subscribes, it actually gets a value
import 'rxjs/add/observable/of';
MockProductService.emitter = Observable.of(products);
// don't call ngOnitInit. Use detectChanges instead
fixture.detectChanges();
// wait for observable subscription to resolve
fixture.whenStable().then(() => {
// do other stuff
expect(comp.products).toBe(whatever)
})
You're also going to need to handle the getProducts
method on the mock.
As an aside, the EventEmitter
is not really meant to be used for services. For that you should use Subject
s. They're pretty much really the same thing, but it's still recommended not to use EventEmitter
this way. Just google how to use Subject
s. I'm sure there are a bunch of articles/post out there.