测试 Angular 扩展抽象 class 的组件
Testing Angular component extending an abstract class
我对许多组件进行了此设置:
@Directive()
export abstract class BaseComponent implements OnInit {
@Select(PortalState.portal) public portalState: Observable<PortalModel>;
public portal: PortalModel;
protected ngUnsubscribe: Subject<void> = new Subject();
constructor(
protected someService: SomeService,
protected route: ActivatedRoute
){
}
public ngOnInit(): void {
this.route.params
.pipe(
filter(res => !!res),
tap(res => this.id = res['id']),
switchMap(() => this.portalState),
takeUntil(this.ngUnsubscribe)
)
.subscribe(res => {
this.portal = res;
this.afterInit();
});
}
public ngOnDestroy(): void {
this.ngUnsubscribe.next();
this.ngUnsubscribe.complete();
}
public abstract afterInit(): void
}
然后在每个扩展这个基础的组件中 class:
@Component({...})
export class SomeComponent extends BaseComponent {
public specificVar: any;
constructor(
protected someService: SomeService,
protected route: ActivatedRoute
){
super(someService, route);
}
public afterInit(): void {
/** do component specific stuff */
this.specificVar = 'something';
}
}
现在它工作正常,但是在测试时,抽象组件的 ngOnInit 似乎根本没有被调用渲染(在这个例子中)this.specific
变量未定义。
spec 文件看起来很传统
let store: Store; /** Ngxs store */
const mockPortalModel: PortalModel = {id: 1};
beforeEach(async () => {
TestBed.configureTestingModule(/** providers and usual setup */);
store = TestBed.inject(Store);
store.reset({
portalState: mockPortalModel
});
jest.spyOn(store, 'dispatch');
jest.spyOn(store, 'select');
});
beforeEach(() => {
fixture = TestBed.createComponent(SomeComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
知道为什么 ngOnInit
和随后的 afterInit
没有被调用吗?
您必须在 SomeComponent
中编写以下函数,以便它 运行 您的 BaseComponent
的 ngOnInit
方法在 SomeComponent
的 ngOnInit
方法。
ngOnInit() {
super.ngOnInit();
}
问题出在必须模拟的 this.route.params
中,因此订阅可能会触发。所以模拟激活的路由已经解决了这个问题:
const mockActivatedRoute = {
snapshot: {}, // needs to be there to avoid _lastPathIndex error
params: of({id: 123})
}
/** ... */
providers: [
{provide: ActivatedRoute, useValue: mockActivatedRoute }
]
已确保订阅通过(请参阅 filter(res => !!res)
- 它就在那里停止)。
我对许多组件进行了此设置:
@Directive()
export abstract class BaseComponent implements OnInit {
@Select(PortalState.portal) public portalState: Observable<PortalModel>;
public portal: PortalModel;
protected ngUnsubscribe: Subject<void> = new Subject();
constructor(
protected someService: SomeService,
protected route: ActivatedRoute
){
}
public ngOnInit(): void {
this.route.params
.pipe(
filter(res => !!res),
tap(res => this.id = res['id']),
switchMap(() => this.portalState),
takeUntil(this.ngUnsubscribe)
)
.subscribe(res => {
this.portal = res;
this.afterInit();
});
}
public ngOnDestroy(): void {
this.ngUnsubscribe.next();
this.ngUnsubscribe.complete();
}
public abstract afterInit(): void
}
然后在每个扩展这个基础的组件中 class:
@Component({...})
export class SomeComponent extends BaseComponent {
public specificVar: any;
constructor(
protected someService: SomeService,
protected route: ActivatedRoute
){
super(someService, route);
}
public afterInit(): void {
/** do component specific stuff */
this.specificVar = 'something';
}
}
现在它工作正常,但是在测试时,抽象组件的 ngOnInit 似乎根本没有被调用渲染(在这个例子中)this.specific
变量未定义。
spec 文件看起来很传统
let store: Store; /** Ngxs store */
const mockPortalModel: PortalModel = {id: 1};
beforeEach(async () => {
TestBed.configureTestingModule(/** providers and usual setup */);
store = TestBed.inject(Store);
store.reset({
portalState: mockPortalModel
});
jest.spyOn(store, 'dispatch');
jest.spyOn(store, 'select');
});
beforeEach(() => {
fixture = TestBed.createComponent(SomeComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
知道为什么 ngOnInit
和随后的 afterInit
没有被调用吗?
您必须在 SomeComponent
中编写以下函数,以便它 运行 您的 BaseComponent
的 ngOnInit
方法在 SomeComponent
的 ngOnInit
方法。
ngOnInit() {
super.ngOnInit();
}
问题出在必须模拟的 this.route.params
中,因此订阅可能会触发。所以模拟激活的路由已经解决了这个问题:
const mockActivatedRoute = {
snapshot: {}, // needs to be there to avoid _lastPathIndex error
params: of({id: 123})
}
/** ... */
providers: [
{provide: ActivatedRoute, useValue: mockActivatedRoute }
]
已确保订阅通过(请参阅 filter(res => !!res)
- 它就在那里停止)。