Angular unit testing. TypeError: Cannot read property 'id' of null. [Jasmine, Karma]

Angular unit testing. TypeError: Cannot read property 'id' of null. [Jasmine, Karma]

我正在为我的 Angular 应用程序编写组件单元测试。我在我的组件和 html 模板中使用了一个 currentUser 变量。它在每次测试中都被模拟(通过 component.currentUser = {...} 硬编码)。无论如何,业力失败了。非常感谢任何帮助。

UserProfileComponent > should create
TypeError: Cannot read property 'id' of null
    at UserProfileComponent.ngOnInit (http://localhost:9877/_karma_webpack_/webpack:/src/app/modules/user-profile/page/user-profile/user-profile.component.ts:4:21)
    at callHook (http://localhost:9877/_karma_webpack_/webpack:/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:2573:1)
    at callHooks (http://localhost:9877/_karma_webpack_/webpack:/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:2542:1)
    at executeInitAndCheckHooks (http://localhost:9877/_karma_webpack_/webpack:/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:2493:1)
    at refreshView (http://localhost:9877/_karma_webpack_/webpack:/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:9481:1)
    at renderComponentOrTemplate (http://localhost:9877/_karma_webpack_/webpack:/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:9580:1)
    at tickRootContext (http://localhost:9877/_karma_webpack_/webpack:/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:10811:1)
    at detectChangesInRootView (http://localhost:9877/_karma_webpack_/webpack:/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:10836:1)
    at RootViewRef.detectChanges (http://localhost:9877/_karma_webpack_/webpack:/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:22815:1)
    at ComponentFixture._tick (http://localhost:9877/_karma_webpack_/webpack:/node_modules/@angular/core/__ivy_ngcc__/fesm2015/testing.js:141:1)

下面的代码示例。

用户-profile.component.ts

ngOnInit(): void {
    this.currentUser = this.userService.getUser();
    console.log(this.currentUser.id);
    this.userAnnouncements();
  }

用户-profile.component.spec.ts

describe('UserProfileComponent', () => {
  let component: UserProfileComponent;
  let fixture: ComponentFixture<UserProfileComponent>;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      imports: [HttpClientTestingModule, RouterTestingModule],
      declarations: [UserProfileComponent],
      schemas: [CUSTOM_ELEMENTS_SCHEMA],
    }).compileComponents();
  });

  beforeEach(() => {
    fixture = TestBed.createComponent(UserProfileComponent);
    component = fixture.componentInstance;
    component.currentUser = {
      id: 1,
      email: 'admin@admin.com',
      firstname: 'admin',
      img_name: 'photo',
      lastname: 'admin',
      location: 1,
      password: 'hashed_pass',
      role: 'admin',
      username: 'admin',
    };
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });
});

问题是您没有模拟 userService 的数据,因此它会覆盖您设置的值。

您应该如下模拟服务:

describe('UserProfileComponent', () => {
  let component: UserProfileComponent;
  let fixture: ComponentFixture<UserProfileComponent>;
  let userService: jasmine.SpyObj<UserService>;
  beforeEach(async () => {
    const userServiceSpy = jasmine.createSpyObj('UserService', ['getUser']);
    await TestBed.configureTestingModule({
      imports: [HttpClientTestingModule, RouterTestingModule],
      declarations: [UserProfileComponent],
      providers: [ {provide: UserService, useValue: userServiceSpy }],
      schemas: [CUSTOM_ELEMENTS_SCHEMA],
    }).compileComponents();
  });

  beforeEach(() => {
    userService = TestBed.inject(UserService) as jasmine.SpyObj<UserService>;
    userService.getUser.and.returnValue({
      id: 1,
      email: 'admin@admin.com',
      firstname: 'admin',
      img_name: 'photo',
      lastname: 'admin',
      location: 1,
      password: 'hashed_pass',
      role: 'admin',
      username: 'admin',
    });
    fixture = TestBed.createComponent(UserProfileComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
    expect(component.currentUser.id).toBe(1)
  });
});

我会推荐你​​阅读the collection of articles to know how to unit test with well defined practice

有一种非常简单的方法可以解决此问题:您可以执行以下操作:

// In your service file, do this: 

@Injectable() 
class MockUserService extends UserService {
  getUser() {
    const mockData = {
      id: 1,
      email: 'admin@admin.com',
      firstname: 'admin',
      img_name: 'photo',
      lastname: 'admin',
      location: 1,
      password: 'hashed_pass',
      role: 'admin',
      username: 'admin',
    };
    
    return mockData;
  }

  // You can add more methods your service provides and mock here
}


describe('UserProfileComponent', () => {
  let component: UserProfileComponent;
  let fixture: ComponentFixture<UserProfileComponent>;
  beforeEach(async () => {
    await TestBed.configureTestingModule({
      imports: [HttpClientTestingModule, RouterTestingModule],
      declarations: [UserProfileComponent],
      providers: [{
         provide: UserService, 
         useClass: MockUserService 
      }],
      schemas: [CUSTOM_ELEMENTS_SCHEMA],
    }).compileComponents();
  });

  beforeEach(() => {
    fixture = TestBed.createComponent(UserProfileComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
    expect(component.currentUser.id).toBe(1)
  });

  it('should run #ngOnInit() method', () => {
     spyOn(component, 'ngOnInit').and.callThrough();
     component.ngOnInit();
     expect(component.ngOnInit).toHaveBeenCalled(); 
  });
});