Home > Back-end >  How to mock observable property in injected service?
How to mock observable property in injected service?

Time:04-11

Please, help me to fix the test, I have no idea how to fix it. Looks like there are different instances of the injected service in the streams. I've created simplified version of my code, here it is.

I'm trying to mock observable facade.dataA$ = of(999); but this part is ignored at all.

Current Results: enter image description here

import { Injectable } from "@angular/core";
import { TestBed, waitForAsync } from '@angular/core/testing';
import { Observable, of } from "rxjs";
import { map, withLatestFrom } from "rxjs/operators";

export class OptionsFacade {
    dataA$: Observable<number> = of(100);
    dataB$: Observable<number> = of(500);
  }
  
  @Injectable()
  export class TestService {
    private options$: Observable<number[]> = this.optionsFacade.dataA$.pipe(
      withLatestFrom(this.optionsFacade.dataB$),
      map(([ valueA, valueB ]) => {
        return [ valueA, valueB ];
      }),
    );
  
    constructor(private optionsFacade: OptionsFacade) {
    }
  
    getOptions(): Observable<any> {
      return this.options$.pipe(
        map(([ val1, val2 ]) => {
          return { val1, val2 };
        }),
      );
    }
  }
  
  describe('TestService', () => {
    let service: TestService;
    let facade: OptionsFacade;
  
    beforeAll(() => {
      TestBed.configureTestingModule({
        providers: [
          TestService,
          OptionsFacade,
        ],
      });
  
      service = TestBed.inject(TestService);
      facade = TestBed.inject(OptionsFacade);
    });
  
    describe('getServerSelectedOptionsUids', () => {
      it('should be array with one item', waitForAsync(() => {
        facade.dataA$ = of(999);
  
        service.getOptions().subscribe(data => {
          expect(data).toEqual({ val1: 999, val2: 500 });
        });
      }));
    });
  });
  

CodePudding user response:

OptionsFacade properties should be of type BehaviorSubject - a stream that is both observer and observable. Then you can assign new values to them. Observable is a read only stream.

About subjects in RxJs documentation.

The assignment is done by calling .next(value) (see comments in my code below)

So your code should be:

export class OptionsFacade {
    dataA$ = new BehaviorSubject<number>(100); // HERE
    dataB$ = new BehaviorSubject<number>(500);
}

@Injectable({ providedIn: 'any' })
export class TestService {
    private options$: Observable<number[]> = this.optionsFacade.dataA$.pipe(
        withLatestFrom(this.optionsFacade.dataB$),
        map(([valueA, valueB]) => [valueA, valueB]),
    );

    constructor(private optionsFacade: OptionsFacade) {
    }

    getOptions(): Observable<any> {
        return this.options$
            .pipe(
                map(([val1, val2]) => ({ val1, val2 })
                ));
    }
}

describe('TestService', () => {
    let service: TestService;
    let facade: OptionsFacade;

    beforeAll(() => {
        TestBed.configureTestingModule({
            providers: [
                TestService,
                OptionsFacade
            ],
        });

        service = TestBed.inject(TestService);
        facade = TestBed.inject(OptionsFacade);
    });

    describe('getServerSelectedOptionsUids', () => {
        it('should be array with one item', waitForAsync(() => {
            facade.dataA$.next(999); // AND HERE

            service.getOptions().subscribe(data => {
                expect(data).toEqual({ val1: 999, val2: 500 });
            });
        }));
    });
});
  • Related