Testing RxJS observables with Promise – the simplest way

When testing RxJS code, you typically want to verify that your observables emit the expected values in response to certain actions or events. See how you can cover those cases using async/await approach.

December 25, 2023 rxjstesting

Testing a RxJS stream with promise

When a observable emits many values writing a testing scenario can be difficult. One of the official solution is to use marble diagrams but is may be overkill for simple scenarios. Better idea can be transforming a RxJS stream into promise and using async/await keywords.

1
import { interval, take, toArray, firstValueFrom } from "rxjs";
2
3
it("should return first tree values", async () => {
4
const interval$ = interval(1_000);
5
const firstThreeValues = await firstValueFrom(interval$.pipe(take(3), toArray()));
6
expect(firstThreeValues).toEqual([0, 1, 2]);
7
});

Read case scenario

Imagine a situation where you have written a store to manage tasks in your application.

1
interface Task {
2
id: number;
3
description: string;
4
completed: boolean;
5
}
6
7
export class TaskStore {
8
private static lastId = 0;
9
10
private tasks$$ = new BehaviorSubject<Task[]>([]);
11
tasks$ = this.tasks$$.asObservable();
12
completedTasks$ = this.tasks$$.pipe(map((toDos) => toDos.filter((task) => task.completed)));
13
14
addTask(description: string): Task {
15
const task: Task = {
16
id: ++TaskStore.lastId,
17
completed: false,
18
description,
19
};
20
21
this.tasks$$.next([task, ...this.tasks$$.getValue()]);
22
23
return task;
24
}
25
26
completeTask(id: number): void {
27
const tasks = this.tasks$$.getValue();
28
const newTasks = tasks.map((task) => (task.id === id ? { ...task, completed: true } : task));
29
30
this.tasks$$.next(newTasks);
31
}
32
}

Let us write a test where we will try to check if our class correctly stores added tasks. To do this we will use promises.

1
it("should add two tasks", async () => {
2
const store = new TaskStore();
3
4
const initialState = await firstValueFrom(store.tasks$);
5
expect(initialState.length).toEqual(0);
6
7
store.addTask("Buy some milk");
8
store.addTask("Buy two eggs");
9
10
const nextState = await firstValueFrom(store.tasks$);
11
expect(nextState.length).toEqual(2);
12
});

firstValueFrom function converts observable to promise by returning the first emitted value.

Do you like the content?

Your support helps me continue my work. Please consider making a donation.

Donations are accepted through PayPal or Stripe. You do not need a account to donate. All major credit cards are accepted.

Leave a comment