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.
import { interval, take, toArray, firstValueFrom } from "rxjs";
it("should return first tree values", async () => {
const interval$ = interval(1_000);
const firstThreeValues = await firstValueFrom(interval$.pipe(take(3), toArray()));
expect(firstThreeValues).toEqual([0, 1, 2]);
});
Read case scenario
Imagine a situation where you have written a store to manage tasks in your application.
interface Task {
id: number;
description: string;
completed: boolean;
}
export class TaskStore {
private static lastId = 0;
private tasks$$ = new BehaviorSubject<Task[]>([]);
tasks$ = this.tasks$$.asObservable();
completedTasks$ = this.tasks$$.pipe(map((toDos) => toDos.filter((task) => task.completed)));
addTask(description: string): Task {
const task: Task = {
id: ++TaskStore.lastId,
completed: false,
description,
};
this.tasks$$.next([task, ...this.tasks$$.getValue()]);
return task;
}
completeTask(id: number): void {
const tasks = this.tasks$$.getValue();
const newTasks = tasks.map((task) => (task.id === id ? { ...task, completed: true } : task));
this.tasks$$.next(newTasks);
}
}
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.
it("should add two tasks", async () => {
const store = new TaskStore();
const initialState = await firstValueFrom(store.tasks$);
expect(initialState.length).toEqual(0);
store.addTask("Buy some milk");
store.addTask("Buy two eggs");
const nextState = await firstValueFrom(store.tasks$);
expect(nextState.length).toEqual(2);
});
firstValueFrom function converts observable to promise by returning the first emitted value.