Skip to main content

Testing

This document provides a brief overview on how to set up unit tests with jest when using the @rx-angular/state package.

info

Make sure you are familiar with the basics of how to test Angular applications, you can read more about it in the official testing docs. It is also recommended to read the official component testing docs.

Basic Testing

There are two ways you can test your state. Depending on your use case, you maybe want to test the behavior of a whole component, or test the state and it's transformations on its own.

tip
  • Note that you want your tests to be unrelated to implementation details as much as possible.
  • Keep in mind that testing a component with DOM nodes instead of component instance is considered as a best practice.
/src/counter.component.ts
import { rxState } from '@rx-angular/state';
import { Component } from '@angular/core';
import { Subject, map, merge } from 'rxjs';
import { AsyncPipe } from '@angular/common';

@Component({
selector: 'rx-counter',
standalone: true,
imports: [AsyncPipe],
template: `
<button id="increment" (click)="increment.next()">Increment</button>
<button id="decrement" (click)="decrement.next()">Decrement</button>
<div id="counter">{{ count$ | async }}</div>
`,
})
export class CounterComponent {
readonly increment = new Subject<void>();
readonly decrement = new Subject<void>();

readonly state = rxState<{ count: number }>(({ connect, set }) => {
set({ count: 0 });
connect(
'count',
merge(
this.increment.pipe(map(() => 1)),
this.decrement.pipe(map(() => -1))
),
({ count }, slice) => count + slice
);
});

readonly count$ = this.state.select('count');
}

Testing State with Marble Diagrams

If you want to test the state and its transformations on its own, you can use the TestScheduler from rxjs/testing to test your state with marble diagrams.

/src/counter.state.ts
import { rxState } from '@rx-angular/state';
import { Injectable } from '@angular/core';
import { Subject, map, merge } from 'rxjs';

@Injectable()
export class CounterService {
readonly increment = new Subject<void>();
readonly decrement = new Subject<void>();

private readonly state = rxState<{ count: number }>(({ connect, set }) => {
set({ count: 0 });
connect(
'count',
merge(
this.increment.pipe(map(() => 1)),
this.decrement.pipe(map(() => -1))
),
({ count }, slice) => count + slice
);
});

readonly count$ = this.state.select('count');
}