【问题标题】:How to mock an observable value from a service Angular 7?如何模拟来自服务 Angular 7 的可观察值?
【发布时间】:2019-09-21 22:42:17
【问题描述】:

我一直在尝试为我的 Angular 组件编写单元测试。目前在我的服务调用中以获取我的组件的数据,我有一个 observable,一旦调用完成,它就会被赋予 true。在我的组件中订阅了这个 observable,因此组件知道调用何时完成。我已经设法在我的组件中模拟了对数据的调用,但我正在努力寻找一种方法来模拟单个可观察值。

我能找到的关于 SO 的所有问题都是关于从组件中的服务模拟函数调用,但我找不到关于模拟单个 observable 的问题。

这是我在服务中的函数调用。正如你所看到的,一旦finalize 函数运行,observable 就会被赋予一个新值:

  public getSmallInfoPanel(key: string): BehaviorSubject<InfoPanelResponse> {

    if (key) {
      this.infoPanel = new BehaviorSubject<InfoPanelResponse>(null);

      this.http.get(`${this.apiUrl}api/Panels/GetInfoPanel/${key}`).pipe(
          retry(3),
          finalize(() => {
            this.hasLoadedSubject.next(true);
          }))
        .subscribe((x: InfoPanelResponse) => this.infoPanel.next(x));
    }
    return this.infoPanel;
  }

这是我在服务中创建Observable 的方式:

 private hasLoadedSubject: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
 public hasLoadedObs: Observable<boolean> = this.hasLoadedSubject.asObservable();

然后在我的组件中订阅从BehaviourSubject 创建的Observable

public hasLoaded: boolean;

  ngOnInit() {

    this.infoPanelSmallService.hasLoadedObs.subscribe(z => this.hasLoaded = z);

  }

当我运行ng test 时,组件测试失败,因为它不知道hasLoadedObs 是什么所以它无法订阅它。

如果我能提供更多信息,请告诉我。谢谢。

更新 1

describe('InformationPanelSmallComponent', () => {
  let component: InformationPanelSmallComponent;
  let fixture: ComponentFixture<InformationPanelSmallComponent>;

  let mockInfoPanelService;
  let mockInfoPanel: InfoPanel;
  let mockInfoPanelResponse: InfoPanelResponse;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [
        RouterTestingModule,
        FontAwesomeModule,
        HttpClientTestingModule
      ],
      declarations: [InformationPanelSmallComponent, CmsInfoDirective],
      providers: [
        { provide: InfoPanelSmallService, useValue: mockInfoPanelService }
      ]
    })
    .compileComponents();
  }));

  beforeEach(() => {

    mockInfoPanel = {
      Title: 'some title',
      Heading: 'some heading',
      Description: 'some description',
      ButtonText: 'some button text',
      ButtonUrl: 'some button url',
      ImageUrl: 'some image url',
      Key: 'test-key',
      SearchUrl: '',
      VideoUrl: ''
    }

    mockInfoPanelResponse = {
      InfoPanel: mockInfoPanel
    }

    fixture = TestBed.createComponent(InformationPanelSmallComponent);
    component = fixture.componentInstance;

    mockInfoPanelService = jasmine.createSpyObj(['getSmallInfoPanel']);

    component = new InformationPanelSmallComponent(mockInfoPanelService);

    component.key = "test-key"

  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });
  //TO DO
  it('should get info panel from info panel service', () => {
    mockInfoPanelService.getSmallInfoPanel.and.returnValue(of(mockInfoPanelResponse));
    component.ngOnInit();

    expect(mockInfoPanelService.getSmallInfoPanel).toHaveBeenCalled();
    expect(component.infoPanel).toEqual(mockInfoPanel);
  });
});


【问题讨论】:

  • 为其添加间谍:spyOnProperty(component.infoPanelSmallService, 'hasLoadedObs', 'get').and.returnValue(of(true))
  • @enno.void 它仍然说hasLoadedObs 未定义。
  • @DanielBailey 您是在嘲笑这项服务,还是在您的 TestBed 中提供您的原始服务?
  • 你能分享你的测试文件吗
  • @RuiMarques 经过更多测试后,它看起来与我将内容放入测试文件并从服务中模拟内容的顺序有关。我现在将发布答案。

标签: angular typescript unit-testing jasmine


【解决方案1】:

我发现这与我模拟服务和创建组件的顺序有关。我还使用了TestBed.overrideProvider,这与我上面使用的不同。这是最终生成的测试文件:


describe('InformationPanelSmallComponent', () => {
  let component: InformationPanelSmallComponent;
  let fixture: ComponentFixture<InformationPanelSmallComponent>;

  let mockInfoPanelService;
  let mockInfoPanel: InfoPanel;
  let mockInfoPanelResponse: InfoPanelResponse;

  beforeEach(async(() => {
    mockInfoPanelService = jasmine.createSpyObj(['getSmallInfoPanel', 'hasLoadedObs']);

    TestBed.configureTestingModule({
      imports: [
        RouterTestingModule,
        FontAwesomeModule,
        HttpClientTestingModule
      ],
      declarations: [InformationPanelSmallComponent, CmsInfoDirective, UrlRedirectDirective],
      providers: [
        { provide: 'BASE_URL', useValue: '/' },
        { provide: 'API_URL', useValue: '/' }
      ]
    })

    TestBed.overrideProvider(InfoPanelSmallService, { useValue: mockInfoPanelService });

    TestBed.compileComponents();
  }));

  beforeEach(() => {

    mockInfoPanel = {
      Title: 'some title',
      Heading: 'some heading',
      Description: 'some description',
      ButtonText: 'some button text',
      ButtonUrl: 'some button url',
      ImageUrl: 'some image url',
      Key: 'test-key',
      SearchUrl: '',
      VideoUrl: ''
    }

    mockInfoPanelResponse = {
      InfoPanel: mockInfoPanel
    }

    fixture = TestBed.createComponent(InformationPanelSmallComponent);
    component = fixture.componentInstance;

    mockInfoPanelService.getSmallInfoPanel.and.returnValue(of(mockInfoPanelResponse));
    mockInfoPanelService.hasLoadedObs = of(true);

    component.key = "test-key"

    fixture.detectChanges();

  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });

  describe('ngOnInit', () => {
    it('should get info panel from info panel service', () => {
      expect(component.hasLoaded).toEqual(true);
      expect(mockInfoPanelService.getSmallInfoPanel).toHaveBeenCalled();
      expect(component.infoPanel).toEqual(mockInfoPanel);
    });

    it('should get loaded is true from service', () => {
      expect(component.hasLoaded).toEqual(true);
    });
  });
});

然后我没有再出现错误,测试实际上运行正常。感谢@RuiMarques 和其他人的所有意见。

【讨论】:

  • 将 mockInfoPanelService.hasLoadedObs 重新分配为可观察的 after 您刚刚将其定义为带有jasmine.createSpyObj 的间谍似乎很奇怪,您正在用可观察的对象覆盖间谍,所以首先创建间谍有什么意义?这可行,但我发现答案并不令人满意,我发现的另一种方法是使用“存根”方法,然后您可以简单地定义间谍并让它们返回您想要的值:shashankvivek-7.medium.com/…
猜你喜欢
  • 2021-06-06
  • 1970-01-01
  • 1970-01-01
  • 2016-09-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-31
  • 2019-03-15
相关资源
最近更新 更多