I want to test an Angular service, I will simplify my case in order to make it straightforward :
I have a behaviorSubject bound to an observable in my service
@Injectable({
providedIn: 'root',
})
export class MyService {
readonly username = new BehaviorSubject<string>('');
readonly username$ = this.username.asObservable();
constructor() {}
}
What I want to test is a simple user interaction on filling the username multiple times :
describe('MyService', () => {
let myService: MyService;
const testScheduler = new TestScheduler((actual, expected) => {
expect(actual).toEqual(expected);
});
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [{ provide: MyService, useValue: myService }]
});
httpTestingController = TestBed.inject(HttpTestingController);
});
afterEach(() => {
// After every test, assert that there are no more pending requests.
httpTestingController.verify();
});
it('should be created', inject([MyService], (service: MyService) => {
expect(service).toBeTruthy();
}));
it('should be toto lala', inject([MyService], (service: MyService) => {
testScheduler.run(helpers => {
const { expectObservable, cold } = helpers;
service.username.next("toto");
service.username.next("lala");
const expect$ = "ab";
expectObservable(service.username$).toBe(expect$, {
a: "toto",
b: "lala",
})
});
}));
But when I launch this test, only the last value emitted by the subject is in the observable.
Chrome 97.0.4692.99 (Windows 10) MyService should be toto lala FAILED
Expected $.length = 1 to equal 2.
Expected $[0].notification.value = 'lala' to equal 'toto'.
Expected $[1] = undefined to equal Object({ frame: 1, notification: Notification({ kind: 'N', value: 'lala', error: undefined, hasValue: true }) }).
Error: Expected $.length = 1 to equal 2.
Expected $[0].notification.value = 'lala' to equal 'toto'.
Expected $[1] = undefined to equal Object({ frame: 1, notification: Notification({ kind: 'N', value: 'lala', error: undefined, hasValue: true }) }).
at <Jasmine>
at TestScheduler.assertDeepEqual (mycomponent.component.spec.ts:71:24)
at node_modules/rxjs/_esm2015/internal/testing/TestScheduler.js:110:1
at <Jasmine>
Chrome 97.0.4692.99 (Windows 10): Executed 1 of 7 (1 FAILED) (0 secs / 0.024 secs)
Chrome 97.0.4692.99 (Windows 10) MyService should be toto lala FAILED
Expected $.length = 1 to equal 2.
Expected $[0].notification.value = 'lala' to equal 'toto'.
Expected $[1] = undefined to equal Object({ frame: 1, notification: Notification({ kind: 'N', value: 'lala', error: undefined, hasValue: true }) }).
Error: Expected $.length = 1 to equal 2.
Expected $[0].notification.value = 'lala' to equal 'toto'.
Expected $[1] = undefined to equal Object({ frame: 1, notification: Notification({ kind: 'N', value: 'lala', error: undefined, hasValue: true }) }).
at <Jasmine>
at TestScheduler.assertDeepEqual (mycomponent.component.spec.ts:71:24)
at node_modules/rxjs/_esm2015/internal/testing/TestScheduler.js:110:1
Chrome 97.0.4692.99 (Windows 10): Executed 2 of 7 (1 FAILED) (skipped 5) (0.126 secs / 0.029 secs)
TOTAL: 1 FAILED, 1 SUCCESS
TOTAL: 1 FAILED, 1 SUCCESS
npm ERR! Test failed. See above for more details.
Is there a way to get 2 values in the observable without changing the BehaviorSubject to a ReplaySubject ?
No that is not possible,
behaviourSubjectcan cache only last value. If you just want to test the subscription and for redundancy I see you next twice, why not next and expect it right after but do it twice.