简体   繁体   English

模拟服务返回未定义的嵌套服务

[英]Mock service return undefined with nested services

I'm currently working on an Angular project and I am creating unit testing for a component using Karma + Jasmine, so I have HTML that has a ngIf calling the API Service as:我目前正在处理一个 Angular 项目,我正在使用 Karma + Jasmine 为组件创建单元测试,所以我有 HTML,它有一个ngIf调用 API 服务:

HTML HTML

<div class="row" *ngIf="apiService.utilsService.userBelongsTo('operations')"></div">

The service in the *ngIf is the service that I want to mock on the spec.ts below *ngIf中的服务是我想在下面的spec.ts上模拟的服务

TS TS

export class CashFlowSalariesComponent implements OnInit, OnChanges {
constructor(
public apiService: ApiService,
) {}

SPECT.TS斯佩克特

 describe('CashFlowSalariesComponent', () => { let fixture: ComponentFixture < CashFlowSalariesComponent >; let mockApiService; let data; beforeEach(async(() => { data = [{ id: 1006, role: "Developer", ... }] mockApiService = jasmine.createSpyObj(['userBelongsTo']) TestBed.configureTestingModule({ schemas: [CUSTOM_ELEMENTS_SCHEMA], imports: [ RouterTestingModule, FormsModule, ReactiveFormsModule, BrowserModule, HttpClientTestingModule, ToastrModule.forRoot({ positionClass: 'toast-bottom-right' }) ], declarations: [ CashFlowSalariesComponent, ], providers: [{ provide: ApiService, useValue: mockApiService }, UserService, ProfileService, VettingStatusService, ApplicationRoleService, SeniorityLevelService, PlacementStatusService, EducationLevelService, UtilsService, ShirtSizeService, CountryService, CityService, PostalCodeService, StateService, ClientSectorService, JobService, ProfileActivityService, ProfileSalaryActivityService, ClientService, RequestTimeOffService, TimeOffTypeService, PulsecheckDetailService, PulsecheckMasterService, PulsecheckQuestionService, ExpenseService, DepartmentService, ExchangeRateService, SkillCategoriesService, ProfileRoleService, ToastrService ] }) fixture = TestBed.createComponent(CashFlowSalariesComponent); })); it('should set salaries data correctly', () => { mockApiService.userBelongsTo.and.returnValue(of('operations')) debugger; fixture.detectChanges(); })

As you see, I tried to create mock of api service as: mockApiService = jasmine.createSpyObj(['userBelongsTo']) , then use in the it as: mockApiService.userBelongsTo.and.returnValue(of('operations')) , but when I debug it throws as unknown as the following picture如您所见,我尝试将 api 服务的模拟创建为: mockApiService = jasmine.createSpyObj(['userBelongsTo']) ,然后在it使用: mockApiService.userBelongsTo.and.returnValue(of('operations')) ,但是当我调试时它抛出如下图所示的未知

introducir la descripción de la imagen aquí

and the test return the following error:并且测试返回以下错误:

Cannot read properties of undefined (reading 'userBelongsTo')无法读取未定义的属性(读取“userBelongsTo”)

I do not know if this happen because userBelongs to it is inside another service inside apiService:我不知道这是否会发生,因为 userBelongs 位于 apiService 中的另一个服务中:

ApiService API服务

@Injectable()
export class ApiService {
 public utilsService: UtilsService;

constructor(private injector: Injector) {
this.utilsService = injector.get(UtilsService);
}
}

Utils.Service:实用程序服务:

userBelongsTo(groupName: string) {
    return this.groups.split(',').reduce((c, g) => c || g.toUpperCase() == groupName.toUpperCase(), false);
  }

How can I make this work?我怎样才能使这项工作? Regards问候

Dependency injections should be private , why does the template HTML needs to handle the service call?依赖注入应该是private的,为什么模板 HTML 需要处理服务调用? Instead of delegating the service call to the template, make a proper function in the component, as a result, your template will be cleaner不要将服务调用委托给模板,而是在组件中创建一个正确的 function,这样,您的模板会更干净

<div class="row" *ngIf="getBelongsTo()"></div">
constructor(private apiService: ApiService) {}

getBelongsTo(): boolean {
  // I guess userBelongsTo returns either a string or undefined,
  // so using !! creates a valid boolean
  // if userBelongsTo returns a valid string, returns TRUE
  // if returns undefined/null returns FALSE
  return !!this.apiService.utilsService.userBelongsTo('operations');
}

For testing, you need to provide the mock/fake value from userBelongsTo before starting to test.对于测试,您需要在开始测试之前userBelongsTo提供mock/fake值。 Additionally, the way you are mocking the service is wrong, it could be like the following:另外,你的方式 mocking 服务是错误的,可能是这样的:

const mockApiService = jasmine.createSpyObj<ApiService>('ApiService', ['userBelongsTo']);
let data;

beforeEach(() => {
  data = [{ id: 1006, role: "Developer", ...}];
  mockApiService.userBelongsTo.and.returnValue(of('operations'));
}

beforeEach(async() => {
  TestBed.configureTestingModule({
    declarations: [...],
    provides: [{provide: ApiService, useValue: mockApiService}]
  })
})

I do not know if this happen because userBelongs to it is inside another service inside apiService我不知道这是否会发生,因为 userBelongs 位于 apiService 中的另一个服务中

When unit testing, you don't care how any dependecy is implemented when is injected in the component, you are mocking all dependencies so it doesn't matter.进行单元测试时,您不关心在组件中注入时如何实现任何依赖项,您是 mocking 所有依赖项,所以没关系。

One thing to notice, since you provided how ApiService is implemented, keep in mind that somewhere, in some module , ApiService needs to be added in the providers array since it isn't provided as root , ex: @Injectable({ provideIn: 'root' })需要注意的一件事是,由于您提供了ApiService的实现方式,请记住,在某个module的某处,需要将ApiService添加到providers数组中,因为它不是作为root提供的,例如: @Injectable({ provideIn: 'root' })

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM