Reputation: 103
I have to write unit tests for an angular 5 application. In order to do that I use jasmine + jest
(we use jest instead of karma in my company for test speed).
In order to test my component behaviour (see code below), I create a test that subscribe to the same Observable
as the component under test, then, wait 2 seconds in the hope that the component's subscription code block had enougth time to complete, and then, look for component's internal changes.
The problem is that as the number of test increase, the time needed for the tests to complete increase too. And I personally think that there must be a better way of testing this type of code below.
async
but couldn't find a way to make it fit my needs. Thanks in advance for your help.
import { Component, OnInit, OnDestroy } from '@angular/core';
import { SomeService } from './some.service';
@Component({
selector: 'app-dummy',
templateUrl: './dummy.component.html',
styleUrls: ['./dummy.component.scss']
})
export class DummyComponent implements OnInit, OnDestroy {
isEditable: Boolean;
//...
private aSubscriber;
constructor(private someService: SomeService) {
this.aSubscriber = someService.anObservable$.subscribe(value => {
this.isEditable = value;
});
}
ngOnInit() { }
ngOnDestroy() {
this.aSubscriber.unsubscribe();
}
}
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';
@Injectable()
export class SomeService {
private aSubject = new Subject<any>();
anObservable$ = this.aSubject.asObservable();
constructor() { }
notify(value) {
this.aSubject.next(value);
}
}
import { async, ComponentFixture, TestBed, inject } from '@angular/core/testing';
import { DummyComponent } from './dummy.component';
import { SomeService } from './some.service';
describe('DummyComponent', () => {
let component: DummyComponent;
let fixture: ComponentFixture<DummyComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [DummyComponent],
providers: [SomeService]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(DummyComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
it('should subscribe to anObservable and set values according to the received one',
inject([SomeService], (service: SomeService) => {
service.anObservable$.subscribe(value => {
setTimeout(() => { }, 2000);
//Test that values are correctly set in the component under test.
expect(component.isEditable).toBeTruthy();
//...
});
service.notify(true);
}));
});
Upvotes: 0
Views: 2138
Reputation: 30088
I often find that my code can be refactored somewhat to accommodate testing, and that refactoring typically makes the code less tightly coupled, more flexible, and easier to read.
In that vein, I would suggest that you take the code that you currently have in the "next" handler of your subscription, and extract it out to a separate method.
For instance, take
constructor(private someService: SomeService) {
this.aSubscriber = someService.anObservable$.subscribe(value => {
this.isEditable = value;
});
}
and refactor it to:
constructor(private someService: SomeService) {
this.aSubscriber = someService.anObservable$.subscribe(value => {
this.onValue(value);
});
}
private onValue(value) {
this.isEditable = value;
}
Then, you can simply test the "onValue" method directly, without having to test the Observable. It is my opinion that unless you are doing something more advanced with your Observable (piping it to a map(), filter() etc.), you don't need to test Observables, and you remove the asynchronous aspect of the test.
And if you do have an advanced pipeline, you should probably be testing it separately. Splitting it up like this enables testing both situations separately.
Upvotes: 6