We use cookies to enhance your experience on the site
CodeWorlds

Testing Asynchronicity and Signals

fakeAsync and tick

1import { fakeAsync, tick, flush, flushMicrotasks } from '@angular/core/testing';
2
3describe('AsyncComponent', () => {
4  it('should update after delay - fakeAsync', fakeAsync(() => {
5    const component = new AsyncComponent();
6
7    component.loadData();  // Sets setTimeout for 1000ms
8
9    expect(component.data).toBeNull();
10
11    tick(500);  // Advance time by 500ms
12    expect(component.data).toBeNull();
13
14    tick(500);  // Another 500ms
15    expect(component.data).toBeTruthy();
16  }));
17
18  it('should handle multiple async operations', fakeAsync(() => {
19    const component = new AsyncComponent();
20
21    component.loadMultiple();  // Multiple setTimeouts
22
23    flush();  // Execute all timers
24
25    expect(component.allLoaded).toBeTrue();
26  }));
27
28  it('should handle promises', fakeAsync(() => {
29    const component = new AsyncComponent();
30
31    component.loadWithPromise();
32
33    flushMicrotasks();  // Resolves Promise
34
35    expect(component.promiseResult).toBeDefined();
36  }));
37});

waitForAsync

1import { waitForAsync } from '@angular/core/testing';
2
3describe('AsyncHttpComponent', () => {
4  it('should fetch data asynchronously', waitForAsync(() => {
5    const fixture = TestBed.createComponent(AsyncHttpComponent);
6    const component = fixture.componentInstance;
7
8    component.fetchData();
9
10    fixture.whenStable().then(() => {
11      fixture.detectChanges();
12      expect(component.data).toBeTruthy();
13    });
14  }));
15});

Testing Signals

1import { signal, computed, effect } from '@angular/core';
2import { TestBed } from '@angular/core/testing';
3
4describe('SignalStore', () => {
5  it('should update signal value', () => {
6    const count = signal(0);
7
8    count.set(5);
9    expect(count()).toBe(5);
10
11    count.update(v => v + 1);
12    expect(count()).toBe(6);
13  });
14
15  it('should compute derived values', () => {
16    const count = signal(10);
17    const doubled = computed(() => count() * 2);
18
19    expect(doubled()).toBe(20);
20
21    count.set(5);
22    expect(doubled()).toBe(10);
23  });
24});
25
26describe('ComponentWithSignals', () => {
27  let component: SignalComponent;
28  let fixture: ComponentFixture<SignalComponent>;
29
30  beforeEach(async () => {
31    await TestBed.configureTestingModule({
32      imports: [SignalComponent]
33    }).compileComponents();
34
35    fixture = TestBed.createComponent(SignalComponent);
36    component = fixture.componentInstance;
37    fixture.detectChanges();
38  });
39
40  it('should update template when signal changes', () => {
41    component.count.set(42);
42    fixture.detectChanges();
43
44    const compiled = fixture.nativeElement;
45    expect(compiled.querySelector('.count').textContent).toContain('42');
46  });
47
48  it('should react to input signal changes', () => {
49    // For input() signals use setInput
50    fixture.componentRef.setInput('name', 'Musashi');
51    fixture.detectChanges();
52
53    expect(fixture.nativeElement.textContent).toContain('Musashi');
54  });
55});

Testing Observables with Marbles (optional)

1import { TestScheduler } from 'rxjs/testing';
2
3describe('RxJS Marble Testing', () => {
4  let scheduler: TestScheduler;
5
6  beforeEach(() => {
7    scheduler = new TestScheduler((actual, expected) => {
8      expect(actual).toEqual(expected);
9    });
10  });
11
12  it('should filter even numbers', () => {
13    scheduler.run(({ cold, expectObservable }) => {
14      const source$ = cold('a-b-c-d-e|', { a: 1, b: 2, c: 3, d: 4, e: 5 });
15      const expected =     '--b---d--|';
16
17      const result$ = source$.pipe(filter(n => n % 2 === 0));
18
19      expectObservable(result$).toBe(expected, { b: 2, d: 4 });
20    });
21  });
22});

Testing Effects

1describe('Effect Testing', () => {
2  it('should trigger effect on signal change', () => {
3    let effectCount = 0;
4
5    TestBed.runInInjectionContext(() => {
6      const count = signal(0);
7
8      effect(() => {
9        count();  // Read signal in effect
10        effectCount++;
11      });
12
13      // First effect run
14      TestBed.flushEffects();
15      expect(effectCount).toBe(1);
16
17      // Signal change
18      count.set(1);
19      TestBed.flushEffects();
20      expect(effectCount).toBe(2);
21    });
22  });
23});
Go to CodeWorlds