[英]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'))
,但是当我调试时它抛出如下图所示的未知
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.