I'm trying to do component integration testing (i.e. including the Angular lifecycle events), but I'm having a hard time mocking a service with Jasmine, one that exposes a Subscribable
.
To reproduce my scenario:
ng new ng-comp-tests
ng generate component my-screen
ng generate service crud
ng generate class item
Update the class to this:
export class Item {
constructor(public name: string) { }
}
Then make the service look like this:
export class CrudService {
private thing = new Subject<Item>();
constructor() {
setInterval(() => { this.thing.next(new Item('Thing ' + Math.random())); }, 1000);
}
getThingObservable() {
return this.thing.asObservable();
}
}
Change the component to be like this (and include it as the main thing in the app component):
<p *ngIf="!!thing">{{thing.name}}</p>
export class MyScreenComponent implements OnInit, OnDestroy {
public thing: Item;
private thingSubscription: Subscription;
constructor(private service: CrudService) { }
ngOnInit() {
this.thingSubscription = this.service
.getThingObservable()
.subscribe(t => { this.thing = t; });
}
ngOnDestroy(): void {
this.thingSubscription.unsubscribe();
}
}
And finally, change the spec to be this:
describe('MyScreenComponent', () => {
let component: MyScreenComponent;
let fixture: ComponentFixture<MyScreenComponent>;
beforeEach(async(() => {
const serviceSpy = jasmine.createSpyObj('CrudService', {
getThingObservable: () => new Subject<Item>().asObservable()
});
TestBed.configureTestingModule({
declarations: [ MyScreenComponent ],
providers: [ { provide: CrudService, useValue: serviceSpy } ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(MyScreenComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
This will give an exception for the test:
MyScreenComponent should create
[object ErrorEvent] thrown
The exception isn't really helpful, and the console from Chrome isn't either, because it shows this abbreviated log:
- "Failed to load ng:///DynamicTestModule/...ngfactory.js: Cross origin requests..."
- "Failed to load ng:///DynamicTestModule/...ngfactory.js: Cross origin requests..."
- "Uncaught DOMException: Failed to execute 'send'...."
- "Error during cleanup of component ... Cannot read property 'unsubscribe' of undefined..."
The top answer for a related question suggests I forgot to do fixture.detectChanges()
(which prevents ngOnInit
from firing), but as you can see I do have that in my code.
There must be something else wrong here, but what?