如何在 angular 中模拟 属性 服务 class (Jasmine/Karma)

How to mock property of a service class in angular (Jasmine/Karma)

我正在尝试从我的服务中模拟 currentOrganizationMessageSource,但出现以下错误:

this.OrganizationService.currentOrganizationMessageSource.subscribe 不是函数

我尝试使用 spyOnProperty 但我得到 currentOrganizationMessageSource 不是 属性

Class:

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

测试规范 class:

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;

将此添加到 beforeach:

orgService= TestBed.get(KhojiOrganizationService);

测试用例:

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

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

你可以试试这个createMockService方法,基于ts-mockery lib

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

那你就可以这样使用了

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

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

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

import { of } from 'rxjs';
const RESULT = [{id: 1, name:"test"},{id: 2, name:"toto"}];
beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [SomeModule, OtherModule],
      declarations: [SomeComponent, MockComponent(OtherComponent)],
      providers: [createMockService(OrganizationService, { yourMethod: () => of(RESULTS) })]
    }).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 提供,在测试注入路由器的 component/services 时有帮助。