I have a service that creates an observable that emits values, and it's relatively easy to test that an emitted value is as expected.
For example:
describe('service', () => {
beforeEach(() => {
TestBed.configureTestingModule({providers: [MyService]});
});
it('should emit true', async(() => {
const service = TestBed.get(MyService);
service.values$.subscribe((value) => expect(value).toBeTruthy());
}));
});
The above works to test the expected value, but it only works if the service actually emits a value. If you have the edge case where the service fails to emit a value, then the test itself actually passes and Jasmine logs the warning message "SPEC HAS NO EXPECTATIONS should be created".
I searched Google for a while trying to figure out how to catch this case as an error and came up with this approach.
it('should emit true', async(() => {
const service = TestBed.get(MyService);
let value;
service.values$.subscribe((v) => value = v);
expect(value).toBeTruthy();
}));
The above works only for synchronous observables and feels like code smell to me. Another developer will see this and think it's a poor quality test.
So after thinking about this for a few days. I thought of using takeUntil()
to force the observable to plete and then test the expected result then.
For example:
describe('service', () => {
let finished: Subject<void>;
beforeEach(() => {
TestBed.configureTestingModule({providers: [MyService]});
finished = new Subject();
});
afterEach(() => {
finished.next();
finishedplete();
});
it('should emit true', async(() => {
const service = TestBed.get(MyService);
let value;
service.changes$
.pipe(
takeUntil(finished),
finalize(() => expect(value).toBeTruthy())
)
.subscribe((v) => value = v);
}));
});
In the above example the value is being stored in a local variable and then the expected result is checked when the observable pletes. I force the pletion by using afterEach()
with takeUntil()
.
Question:
Are there any side effects with my approach, and if so what would be the more Angular/Jasmine way of performing these kinds of tests. I am worried that you are not suppose to perform expect assertions during the afterEach() life-cycle call.
I have a service that creates an observable that emits values, and it's relatively easy to test that an emitted value is as expected.
For example:
describe('service', () => {
beforeEach(() => {
TestBed.configureTestingModule({providers: [MyService]});
});
it('should emit true', async(() => {
const service = TestBed.get(MyService);
service.values$.subscribe((value) => expect(value).toBeTruthy());
}));
});
The above works to test the expected value, but it only works if the service actually emits a value. If you have the edge case where the service fails to emit a value, then the test itself actually passes and Jasmine logs the warning message "SPEC HAS NO EXPECTATIONS should be created".
I searched Google for a while trying to figure out how to catch this case as an error and came up with this approach.
it('should emit true', async(() => {
const service = TestBed.get(MyService);
let value;
service.values$.subscribe((v) => value = v);
expect(value).toBeTruthy();
}));
The above works only for synchronous observables and feels like code smell to me. Another developer will see this and think it's a poor quality test.
So after thinking about this for a few days. I thought of using takeUntil()
to force the observable to plete and then test the expected result then.
For example:
describe('service', () => {
let finished: Subject<void>;
beforeEach(() => {
TestBed.configureTestingModule({providers: [MyService]});
finished = new Subject();
});
afterEach(() => {
finished.next();
finished.plete();
});
it('should emit true', async(() => {
const service = TestBed.get(MyService);
let value;
service.changes$
.pipe(
takeUntil(finished),
finalize(() => expect(value).toBeTruthy())
)
.subscribe((v) => value = v);
}));
});
In the above example the value is being stored in a local variable and then the expected result is checked when the observable pletes. I force the pletion by using afterEach()
with takeUntil()
.
Question:
Are there any side effects with my approach, and if so what would be the more Angular/Jasmine way of performing these kinds of tests. I am worried that you are not suppose to perform expect assertions during the afterEach() life-cycle call.
Share Improve this question asked Dec 18, 2018 at 13:37 ReactgularReactgular 54.9k24 gold badges173 silver badges218 bronze badges1 Answer
Reset to default 6This seems overkill to me.
Jasmine offers a callback in its tests, you could simply use it ?
it('should X', doneCallback => {
myObs.subscribe(res => {
expect(x).toBe(y);
doneCallback();
});
});
If the callback isn't called, the test fails with a timeout exception (meaning no more test will run after this failed one)