【问题标题】:Testing Observable Callback to Change Object State测试 Observable 回调以更改对象状态
【发布时间】:2019-12-18 18:00:51
【问题描述】:

我有一项服务可以执行以下操作:

  updateProperties(properties: any) {
    return this.http.put(environment.adminApiURLPrefix+'api/v1/properties', properties);
  }

在我的组件中,我有两个对象,providers 和 providerProperties。 Providers 包含 API 提供者及其状态的键值对,而 providerProperties 是从 API 返回的实际响应(必须存储,因为任何后续请求都需要整个对象)。

import { Component, OnInit } from '@angular/core';
import { ProviderService } from '../../services/provider.service';
import { Status } from 'src/app/models/status.enum';
import { AlertService } from 'src/app/services/alert.service';

@Component({
  selector: 'app-providers',
  templateUrl: './providers.component.html',
  styleUrls: ['./providers.component.scss']
})
export class ProvidersComponent implements OnInit {

  providers: any;
  providerProperties: any;
  status: Status;

  constructor(private providerService: ProviderService, private alertService: AlertService) {
    this.providers = {
      'API1': false,
      'API2': false,
      'API3': false
    }
    this.status = Status.Loading;
  }

  ngOnInit() {
    this.providerService.getProperties().subscribe((response: any) => {
      this.setProviderValues(response);
      this.status = Status.Ready;
    },
    error => {
      this.alertService.error('Error');
      this.status = Status.Error;
    })
  }

  setProviderValues(response: any) {
    this.providerProperties = response;
    Object.keys(this.providers).forEach(key => {
      let providerStatus: string = this.providerProperties[key.toLowerCase() + '.enabled'];
      if(providerStatus == 'true' || providerStatus == 'false') {
        this.providers[key] = providerStatus == 'true' ? true : false;
      } else {
        this.alertService.error('Error.');
        this.status = Status.Error;
        return;
      }
    });
  }

  changeProviderProperty(provider: string, isEnabled: boolean) {
    let providerKey: string = this.providerDisplayNameToPropertyKey(provider);

    let tempProviderProperties = Object.assign({}, this.providerProperties);
    tempProviderProperties[providerKey] = isEnabled ? true : false;

    this.providerService.updateProperties(tempProviderProperties).subscribe(
      response => {
        this.setProviderValues(response);
        this.alertService.success('Successfully '+ (isEnabled ? 'Enabled ' : 'Disabled ') + provider);
      },
      err => {
        if(err['status'] == 0) {
          this.alertService.error('Error');
        } else {
          let errorArray = err['error']['Errors']['Error'];
          for(let errorKey in errorArray) {
            let errorValue = errorArray[errorKey];
            this.alertService.error('Error Updating Properties: ' + errorValue['ReasonCode'] + ' ' + errorValue['Description']);
          }
        }
      }
    )
  }

  providerDisplayNameToPropertyKey(provider: string) {
    return provider.toLowerCase() + '.enabled';
  }
}

我有以下测试:

import { async, ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
import { HttpClientTestingModule } from '@angular/common/http/testing';

import { ProvidersComponent } from './providers.component';
import { ProviderService } from 'src/app/services/provider.service';
import { of, Observable, throwError } from 'rxjs';
import { AlertComponent } from '../shared/alert/alert.component';
import { RouterTestingModule } from '@angular/router/testing';
import { AlertService } from 'src/app/services/alert.service';
import { HttpErrorResponse } from '@angular/common/http';

describe('ProvidersComponent', () => {
  let component: ProvidersComponent;
  let providerService: ProviderService;
  let alertService: AlertService;
  let fixture: ComponentFixture<ProvidersComponent>;
  let getPropertiesSpy: jasmine.Spy<() => Observable<String>>;
  let changeProviderPropertySpy: jasmine.Spy<(provider: string, isEnabled: boolean) => Promise<void>>;

  let providerProperties:any;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ 
        ProvidersComponent,
        AlertComponent
      ],
      imports: [
        HttpClientTestingModule,
        RouterTestingModule
      ],
      providers: [
        ProviderService,
        AlertService
      ]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    providerService = TestBed.get(ProviderService);
    alertService = TestBed.get(AlertService);
    fixture = TestBed.createComponent(ProvidersComponent);

    providerProperties = {
      'api1.enabled': 'true',
      'api2.enabled': 'true',
      'api3.enabled': 'false',
    };

    component = fixture.componentInstance;

    component.providers = {
      'API1': false,
      'API2': false,
      'API3': false
    };

    getPropertiesSpy = spyOn(providerService, 'getProperties').and.callFake(() => {
      return of(providerProperties);
    });

    changeProviderPropertySpy = spyOn(component, 'changeProviderProperty');

    fixture.detectChanges();
  });

  it('should enable provider', fakeAsync(() => {
    spyOn(providerService, 'updateProperties').and.returnValue(of({
      'api1.enabled': 'true',
      'api2.enabled': 'false',
      'api3.enabled': 'false'
    }));

    component.changeProviderProperty('API1', true);

    fixture.detectChanges();

    expect(component.providers['API1']).toEqual(true);
  }));
});

由于某种原因,对象的状态不会改变。我很确定 observable 没有被订阅或者我没有等待响应。

更新 在监视提供程序服务并检查它是否在我调用 changeProviderProperties 后被调用后,结果发现它从未被调用过。

更新 2 窥探 providerDisplayNameToPropertyKey 方法,发现无论出于何种原因都没有调用它。

【问题讨论】:

  • 您可以随时通过tap操作员或订阅方法中的一些控制台登录来检查您是否正确订阅。

标签: javascript angular typescript jasmine


【解决方案1】:

方法component.changeProviderProperty 包含异步代码。因为您知道这一点,所以您正在fakeAsync 区域中运行您的测试。您缺少的是对tick 的调用,它模拟fakeAsync 区域中计时器的异步时间流逝。

it('should enable provider', fakeAsync(() => {
    ...    
    component.changeProviderProperty('API1', true);
    tick();
    ...
}));

【讨论】:

  • 是的,我觉得这可能是问题所在,但它仍然不起作用。我的间谍正确吗?
  • 更新,我监视了提供者服务,结果无论出于何种原因,在 tick() 之后,没有调用 updateProperties 方法。也许间谍不正确?
  • 乍一看,ProviderService.updateProperties 的间谍看起来不错。我个人也会模拟 AlertService 方法,不知道里面发生了什么。
  • 这只是一个创建警报的简单服务。我不确定在可观察对象方面我是否遗漏了什么。服务应该被模拟还是监视它,然后返回足以执行回调的 observable?
  • 监视方法然后返回 Observable 实际上是在模拟方法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-12-24
  • 1970-01-01
  • 2017-07-08
  • 2014-12-25
  • 1970-01-01
  • 1970-01-01
  • 2016-10-09
相关资源
最近更新 更多