angular - 服务获取数据后对 ngOnInit 内部属性进行单元测试分配

标签 angular unit-testing jasmine karma-jasmine

在 Angular 中进行单元测试时,如何测试 map.component.ts userRole = '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; 
    ...
}

用户楼层.service.ts

export class UserFloorService {
public getUser(): Observable<any> {
    return this.http.get(
      `https://XXX`);
  }
}

map .组件.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();
        });
      });
    });
  });
});

最佳答案

首先,我想解决我看到的两个问题:

  1. 从测试设置中删除 HttpClientModule。您需要的一切都由 HttpClientTestingModule 提供。 UserService 上的 spy 也是如此。 检查这篇文章,了解如何在单元测试中正确模拟 httpClient: https://medium.com/netscape/testing-with-the-angular-httpclient-api-648203820712
  2. ngOnInit 上的异步与 Angular 无关,它仍然将其视为同步。我个人认为在 Angular 生命周期钩子(Hook)上使用异步是一种严重的反模式,因为你会引发大量的竞争条件并且必须对所有内容进行空检查等。由于某种原因,它不断出现;)在那里使用异步没有任何好处,而且会带来很多痛苦。请参阅async/await in Angular `ngOnInit`

话虽如此,您应该能够使用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
  });
}

据我所知,您必须在测试中手动调用 ngOnInit(),它不是由 createComponent(...) 调用的

关于angular - 服务获取数据后对 ngOnInit 内部属性进行单元测试分配,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66662754/

相关文章:

unit-testing - 如何对 Leaflet JS map 进行单元测试?

unit-testing - 在 Browserify 中为单元测试 stub 所需的模块

java - 嵌套 do while 循环的 Junit 测试用例

angularjs - 使用 Jasmine 进行端到端测试

angular - mat-list-item 不计算子组件中的 "mat-line"类

angular - 使用 travis ci 在 headless Chrome 中运行 Angular cli e2e 测试

javascript - Angular Firebase 数据库查询

angular - ngx-bootstrap modal - 通过 bsModalRef.content 访问传递到模态的数据

angular - 如何使用 Protractor typescript 在具有跨度的按钮上单击具有相似元素详细信息的元素

angular - 找不到名称 'viewport'