测试没有生命周期挂钩的组件

时间:2019-07-18 21:51:50

标签: angular typescript unit-testing jasmine karma-jasmine

所以我试图在没有生命周期挂钩的组件上编写测试。

问题是我不知道何时直接像这样分配一个没有生命周期挂钩的值,我不知道何时准确调用它以及为什么在组件初始化时不调用它

component.ts

export class MainNavComponent {
  isLoggedIn: boolean = this.authService.isLoggedIn;
  user: User;

  isHandset$: Observable<boolean> = 
    this.breakpointObserver.observe(Breakpoints.Handset)
    .pipe(
      map(result => result.matches)
    );

constructor(private breakpointObserver: BreakpointObserver,
  public authService: AuthService) {
    //check logged in user
    this.authService.user$.subscribe(res => {
      if(res) {
        this.isLoggedIn = true;
        this.user = res;
      } else {
        this.isLoggedIn = false;
      }
    })
  }
}

component.spec.ts

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

beforeEach(async(() => {
  TestBed.configureTestingModule({
    declarations: [ 
      MainNavComponent,
      FooterComponent
    ],
    imports: [
      MaterialImportsModule,
      RouterTestingModule.withRoutes([]),
      HttpClientModule,
      AngularFireModule.initializeApp(environment.firebase, 'my-app-name'),
      AngularFirestoreModule,
      AngularFireStorageModule,
      AngularFireAuthModule,
      BrowserAnimationsModule
    ] 
  })
  .compileComponents();
}));

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

it('assing return if user is logged in and assign it to isLoggedIn', () => {
    let service: AuthService = TestBed.get(AuthService);    
    spyOn(service, 'isLoggedIn').and.returnValue(true);

    expect(component.isLoggedIn).toBeTruthy();    
  });
});

记录的消息

  

期望假为真。

2 个答案:

答案 0 :(得分:1)

您应该将服务分配转移到beforeEach()方法,并将测试用例添加到fakeAsync()范围。然后,您将使用tick()模拟异步任务的时间流逝。

beforeEach(() => {
  fixture = TestBed.createComponent(MainNavComponent);
  component = fixture.componentInstance;
  service: AuthService = TestBed.get(AuthService); //shift service to beforeEach()
  fixture.detectChanges();
});

...

it('assing return if user is logged in and assign it to isLoggedIn', fakeAsync(() => {

  spyOn(service, 'isLoggedIn').and.returnValue(true);

  tick(); //simulate passage of time that api call went through;
  fixture.detectChanges(); //trigger change detection in Angular to ensure class's isLoggedIn is updated

  expect(service.isLoggedIn).toHaveBeenCalled();
  expect(component.isLoggedIn).toBeTruthy();
}));

关于tick():https://angular.io/guide/testing#the-tick-function

  

调用tick()会模拟时间的流逝,直到所有未完成的异步活动结束为止

第二点,建议不要使用构造函数初始化服务调用。相反,它应该在ngOnInit()中完成。这是为了确保一致性并防止模板中的绑定错误。

取自Difference between Constructor and ngOnInit

  

大多数情况下,我们使用ngOnInit进行所有初始化/声明,并避免某些东西在构造函数中起作用。构造函数应仅用于初始化类成员,而不应执行实际的“工作”。

     

因此,您应该使用构造函数()来设置依赖注入,而无需过多设置。 ngOnInit()是“开始”的更好位置-它是解析组件绑定的位置/时间。

答案 1 :(得分:1)

根据我的看法,您错误地spying

创建一个Mock

export class AuthServiceMock{
     isLoggedIn  = false;
     user$  = new BehaviorSubject<any>({user: 'Hero'});
}

spec文件中的


beforeEach(async(() => {
  TestBed.configureTestingModule({
    declarations: [ 
      MainNavComponent,
      FooterComponent
    ],
    imports: [
      MaterialImportsModule, ..........
    ],
   providers: [{provide: AuthService, useValue: AuthServiceMock}] // add this
  })
  .compileComponents();
}));

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

it('should have isLoggedIn as "true" and "user" as defined ', () => {
    expect(component.user).toBeDefined(); // or use "toEqual"
    expect(component.isLoggedIn).toBeTruthy();    
  });
});