如何在注入服务中模拟 observable 属性?

How to mock observable property in injected service?

请帮我修复测试,我不知道如何修复它。看起来流中有不同的注入服务实例。我已经创建了我的代码的简化版本,就在这里。

我正在尝试模拟 observable facade.dataA$ = of(999);但是这部分完全被忽略了。

当前结果:

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 });
        });
      }));
    });
  });
  

OptionsFacade 属性应该是 BehaviorSubject 类型——一个既是观察者又是可观察者的流。然后您可以为它们分配新的值。 Observable 是只读流。

About subjects in RxJs documentation.

通过调用 .next(value) 完成分配(请参阅下面我的代码中的注释)

所以你的代码应该是:

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 });
            });
        }));
    });
});