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.