如何在angularJasmine / Karma中模拟服务类的属性

问题描述

我正在尝试从服务中模拟 currentOrganizationMessageSource ,但遇到以下错误

this.OrganizationService.currentOrganizationMessageSource.subscribe不是函数

我尝试使用spyOnProperty,然后得到 currentOrganizationMessageSource不是属性

班级:

export class OrganizationEditComponent implements OnInit {

  Feedback: any = {};
  appOrgs: OrganizationDataModel;
  organizationLocationTableTitle: string;


  constructor(
    public route: ActivatedRoute,private router: Router,private orgService: OrganizationService) {
  }


  ngOnInit() {
    this.loadOrganisation();
  }

  private loadOrganisation()
  {
    this.orgService.currentOrganizationMessageSource.subscribe(
      org => this.appOrgs = org);
      this
      .route
      .params
      .pipe(
        map(p => p.id),switchMap(id => {
          if (id === 'new') { return of(this.appOrgs); }
          if (id != null) {
            return this.orgService.findByOrganizationsId(id);
          }
        })
      )
      .subscribe(orgs => {
          this.orgService.changeMessage(orgs);
          this.appOrgs = orgs;
          this.Feedback = {};
        },err => {
          this.Feedback = {type: 'warning',message:'Error'};
        }
      );
  }

服务:

export class OrganizationService {

  private OrganizationMessageSource = new BehaviorSubject( new OrganizationDataModel());
  currentOrganizationMessageSource = this.OrganizationMessageSource.asObservable();


  changeMessage(appOrganization: appOrganizationDataModel) {
    this.appOrganizationMessageSource.next(appOrganization);
  }
}

测试规范类:

fdescribe('OrganizationEditComponent',() => {
  let component: OrganizationEditComponent;
  let fixture: ComponentFixture<OrganizationEditComponent>;
  let activatedRoutes: any = {};


  beforeEach(async(async () => {
    const OrganizationService: OrganizationService = jasmine.createSpyObj('OrganizationService',['currentOrganizationMessageSource']);
    await Testbed.configureTestingModule({
      declarations: [ OrganizationEditComponent ],providers: [
        { provide: ActivatedRoute,useValue: activatedRoutes },{ provide: Router,useValue: {url: ''}},{ provide: OrganizationService,useValue: OrganizationService },],imports: [ FormsModule ],schemas: [CUSTOM_ELEMENTS_SCHEMA,NO_ERRORS_SCHEMA]
    })
    .compileComponents();
    // component.ngOnInit();
    fixture = Testbed.createComponent(OrganizationEditComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  }));

  function givenServicesAreMocked() {
    const spy = Testbed.get(OrganizationService);
    // let mockService = jasmine.createSpyObj(OrganizationService,['currentOrganizationMessageSource']);
    // mockService.currentOrganizationMessageSource.and.returnValue('');
    // spyOnProperty(OrganizationService,"OrganizationMessageSource").and.returnValue([]);
    // OrganizationService.currentOrganizationMessageSource = () => {
    //   return [];
    // };
  }
});

解决方法

使用of运算符。

import { of } from 'rxjs';

在使用前定义orgService:

 let orgService : KhojiOrganizationService;

将此添加到preach:

orgService= TestBed.get(KhojiOrganizationService);

测试用例:

it('should fetch data',() => {
   spyOn(orgService,'currentkhojiOrganizationMessageSource').and.returnValue(of('test Data'));
   component.ngOnInit();
   expect(component.appOrgs).toEqual('test Data');
});

作为示例,我在string运算符内传递了of。您可以在其中传递Object / array of objects并根据数据模型更改期望值。

,

您可以尝试基于ts-mockery lib

的createMockService方法
cudart64_101.dll

然后您可以像这样使用它

import { Mock,Mockery } from 'ts-mockery';

interface Stub {
  [key: string]: any;
}

export function createMockService<T>(service: Type<T>,stubs?: Stub) {
  Mockery.configure('jasmine');
  const mockedFunctions = stubs ? stubs : {};
  if (!stubs) {
    for (const key in service.prototype) {
      mockedFunctions[key] = () => null;
    }
  }
  return {
    provide: service,useValue: Mock.of<Type<T>>(mockedFunctions)
  };
}

这与您的问题无关,但请注意ng-mocks lib中的MockComponent,这是封装业力测试的正确方法,因此仅对组件启用测试,而无需使用[CUSTOM_ELEMENTS_SCHEMA,NO_ERRORS_SCHEMA],这有点不好的做法。

这里是一个示例,您需要使用rxjs运算符来模拟返回的值

beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [SomeModule,OtherModule],declarations: [SomeComponent,MockComponent(OtherComponent)],providers: [createMockService(OrganizationService)]
    }).compileComponents();
  }));

您的模拟服务将始终非常容易以这种方式进行模拟。

,

您的想法正确,但不能使用createSpyObj来模拟属性,而只能使用方法。

import { of } from 'rxjs';
....
// the 2nd argument is for public methods,not properties
    const OrganizationService: OrganizationService = jasmine.createSpyObj('OrganizationService',['changeMessage']);
// attach currentOrganizationMesageSource as a property to OrganizationService and mock the value
OrganizationService.currentOrganizationMessageSource = of(/* Mock the value here */);
// keep everything else the same

那应该可以工作。

您还需要正确模拟ActivatedRouteRouter。对于路由器,只需将RouterTestingModule添加到imports数组中。它由Angular提供,可在测试注入路由器的组件/服务时提供帮助。