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 rxjs testing

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.

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.

Portrait of the author

About me

On a daily basis, I work as a Front-End Developer with a primary focus on JavaScript. I feel most comfortable with Angular and NestJS, but I’m always eager to venture beyond a single tech stack. I effectively leverage AI tools in my workflow, which allows me to quickly onboard into new technologies and write code in other languages or frameworks whenever needed.

After hours, I bring my passion for tech into the physical world. I run my own homelab, where I experiment with server management and build home automation and monitoring powered by Home Assistant.

Leave a comment