服务获取数据后 ngOnInit 中 属性 的单元测试分配
Unit test assignment of property inside ngOnInit after service gets data
在 angular 中进行单元测试时,如何在 map.component.ts userRole = 'Admin' 中测试 属性?现在它在 Expected undefined to equal 'Admin' 上失败了。我尝试模拟使用 testData 获取真实数据的服务。我是单元测试的初学者,请帮忙。谢谢!
map.component.ts
export class MapComponent implements OnInit, OnDestroy {
userRole:string
...
constructor(
private zone: NgZone,
public deskDialog: MatDialog,
private userFloorService: UserFloorService,
private _snackBar: MatSnackBar,
private route: ActivatedRoute,
private router: Router
) {}
async ngOnInit(): Promise<void> {
const userData = await this.userFloorService.getUser().toPromise();
this.userRole = userData.records[0].user_role;
...
}
用户-floor.service.ts
export class UserFloorService {
public getUser(): Observable<any> {
return this.http.get(
`https://XXX`);
}
}
map.component.spec.ts
class UserService {
testData = {
message: 'Succes',
records: [
{
floor_id: 3,
reservations: [
{
desk_id: 11,
reservation_date: '2021-02-22',
reservation_id: 585,
user_id: 7,
},
],
user_gid: 'xxx',
user_id: 7,
user_role: 'Admin',
},
],
};
public getUser(): Observable<any> {
return of(this.testData);
}
}
describe('MapComponent', () => {
let component: MapComponent;
let fixture: ComponentFixture<MapComponent>;
let userService: UserFloorService;
beforeEach(() => {
userService = jasmine.createSpyObj(['getUser']);
TestBed.configureTestingModule({
declarations: [MapComponent],
imports: [
MatSnackBarModule,
AppRoutingModule,
HttpClientModule,
BrowserModule,
MatDialogModule,
HttpClientTestingModule,
],
providers: [{ provide: UserFloorService, useClass: UserService }],
}).compileComponents();
fixture = TestBed.createComponent(MapComponent);
component = fixture.componentInstance;
userService = TestBed.inject(UserFloorService);
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
describe('Given the component is loaded', () => {
describe('When getUser returns mock data', () => {
it('Then the userRole should be Admin', (done) => {
userService.getUser().subscribe(() => {
expect(component.userRole).toEqual('Admin');
done();
});
});
});
});
});
首先,我想解决我看到的问题:
- 从测试设置中删除 HttpClientModule。 HttpClientTestingModule 提供了您需要的一切。对您的 UserService 的间谍也是如此。
检查此 post 以了解如何在单元测试中正确模拟 httpClient:
https://medium.com/netscape/testing-with-the-angular-httpclient-api-648203820712
- 您的 ngOnInit 上的异步与 Angular 无关,它仍然将其视为同步。我个人认为在 angular 生命周期钩子上使用异步是一个严重的反模式,因为你会引入大量的竞争条件并且必须对所有内容进行空检查等。出于某种原因,它不断弹出 ;) 在那里使用 async 没有任何收获,而且会带来很多痛苦。参见
也就是说,您应该能够使用 changeDetectorRef (https://angular.io/api/core/ChangeDetectorRef):
实现您想要的效果
constructor(private readonly cdr: ChangeDetectorRef)
ngOnInit(): void {
this.userFloorService.getUser().subscribe(users => {
this.userRole = userData.records[0].user_role;
this.cdr.detectChanges; // might require markForCheck here, try it out
});
}
而且 afaik 你必须在测试中手动调用 ngOnInit()
,createComponent(...)
不会调用它
所以我能够在不更改 ngOnInit 的情况下使用 beforeEach with async and done() callback in it() :
describe('MapComponent', () => {
let component: MapComponent;
let fixture: ComponentFixture<MapComponent>;
let userService: UserFloorService;
beforeEach(async() => {
await TestBed.configureTestingModule({
declarations: [MapComponent],
imports: [
MatSnackBarModule,
AppRoutingModule,
BrowserModule,
MatDialogModule,
HttpClientTestingModule,
],
providers: [{ provide: UserFloorService, useClass: MockService }],
}).compileComponents();
fixture = TestBed.createComponent(MapComponent);
component = fixture.componentInstance;
userService = TestBed.inject(UserFloorService);
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
it('userRole should be Admin', (done) => {
console.log(done);
expect(component.userRole).toEqual('Admin');
done();
});
});
但是谢谢你的帮助,你给我指明了正确的方向。
在 angular 中进行单元测试时,如何在 map.component.ts userRole = 'Admin' 中测试 属性?现在它在 Expected undefined to equal 'Admin' 上失败了。我尝试模拟使用 testData 获取真实数据的服务。我是单元测试的初学者,请帮忙。谢谢!
map.component.ts
export class MapComponent implements OnInit, OnDestroy {
userRole:string
...
constructor(
private zone: NgZone,
public deskDialog: MatDialog,
private userFloorService: UserFloorService,
private _snackBar: MatSnackBar,
private route: ActivatedRoute,
private router: Router
) {}
async ngOnInit(): Promise<void> {
const userData = await this.userFloorService.getUser().toPromise();
this.userRole = userData.records[0].user_role;
...
}
用户-floor.service.ts
export class UserFloorService {
public getUser(): Observable<any> {
return this.http.get(
`https://XXX`);
}
}
map.component.spec.ts
class UserService {
testData = {
message: 'Succes',
records: [
{
floor_id: 3,
reservations: [
{
desk_id: 11,
reservation_date: '2021-02-22',
reservation_id: 585,
user_id: 7,
},
],
user_gid: 'xxx',
user_id: 7,
user_role: 'Admin',
},
],
};
public getUser(): Observable<any> {
return of(this.testData);
}
}
describe('MapComponent', () => {
let component: MapComponent;
let fixture: ComponentFixture<MapComponent>;
let userService: UserFloorService;
beforeEach(() => {
userService = jasmine.createSpyObj(['getUser']);
TestBed.configureTestingModule({
declarations: [MapComponent],
imports: [
MatSnackBarModule,
AppRoutingModule,
HttpClientModule,
BrowserModule,
MatDialogModule,
HttpClientTestingModule,
],
providers: [{ provide: UserFloorService, useClass: UserService }],
}).compileComponents();
fixture = TestBed.createComponent(MapComponent);
component = fixture.componentInstance;
userService = TestBed.inject(UserFloorService);
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
describe('Given the component is loaded', () => {
describe('When getUser returns mock data', () => {
it('Then the userRole should be Admin', (done) => {
userService.getUser().subscribe(() => {
expect(component.userRole).toEqual('Admin');
done();
});
});
});
});
});
首先,我想解决我看到的问题:
- 从测试设置中删除 HttpClientModule。 HttpClientTestingModule 提供了您需要的一切。对您的 UserService 的间谍也是如此。 检查此 post 以了解如何在单元测试中正确模拟 httpClient: https://medium.com/netscape/testing-with-the-angular-httpclient-api-648203820712
- 您的 ngOnInit 上的异步与 Angular 无关,它仍然将其视为同步。我个人认为在 angular 生命周期钩子上使用异步是一个严重的反模式,因为你会引入大量的竞争条件并且必须对所有内容进行空检查等。出于某种原因,它不断弹出 ;) 在那里使用 async 没有任何收获,而且会带来很多痛苦。参见
也就是说,您应该能够使用 changeDetectorRef (https://angular.io/api/core/ChangeDetectorRef):
实现您想要的效果constructor(private readonly cdr: ChangeDetectorRef)
ngOnInit(): void {
this.userFloorService.getUser().subscribe(users => {
this.userRole = userData.records[0].user_role;
this.cdr.detectChanges; // might require markForCheck here, try it out
});
}
而且 afaik 你必须在测试中手动调用 ngOnInit()
,createComponent(...)
所以我能够在不更改 ngOnInit 的情况下使用 beforeEach with async and done() callback in it() :
describe('MapComponent', () => {
let component: MapComponent;
let fixture: ComponentFixture<MapComponent>;
let userService: UserFloorService;
beforeEach(async() => {
await TestBed.configureTestingModule({
declarations: [MapComponent],
imports: [
MatSnackBarModule,
AppRoutingModule,
BrowserModule,
MatDialogModule,
HttpClientTestingModule,
],
providers: [{ provide: UserFloorService, useClass: MockService }],
}).compileComponents();
fixture = TestBed.createComponent(MapComponent);
component = fixture.componentInstance;
userService = TestBed.inject(UserFloorService);
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
it('userRole should be Admin', (done) => {
console.log(done);
expect(component.userRole).toEqual('Admin');
done();
});
});
但是谢谢你的帮助,你给我指明了正确的方向。