angular - Jasmine/Angular:测试 void EventEmitter onInit 的接收?
问题描述
我正在为我的一个组件编写单元测试。所述组件有一个visible
布尔值,初始化为假。当所述布尔属性变为真时,组件的显示 css 属性从无变为阻塞,使其可见。
由于我的组件是子组件,因此我构建的循环以使父组件在技术上“触发”该组件如下:
- 组件初始化为不可见状态。父母和孩子都连接到如下所示的服务:
export class SidebarService {
public toggleSidebar: EventEmitter<void> = new EventEmitter();
constructor() {}
public showSidebar() {
this.toggleSidebar.emit();
}
}
该服务不需要连接到任何端点,也不需要返回任何数据:发射器充当子组件的“每次点击”信号。
- 点击父模板中的一个按钮,父模板调用如下方法:
toggleSidebar() {
this.sidebarService.showSidebar();
}
- 在我的子组件的 OnInit 上,我写了以下内容:
ngOnInit() {
this.sidebarService.toggleSidebar.subscribe(() => {
this.visible = true;
});
}
这样当父级触发事件发射器时,子级接收它并更改其visible
属性。子组件有自己的手动方法将可见属性重置为 false。
从功能的角度来看,它完成了这项工作:当父组件单击按钮时,组件“出现”。但现在我担心我可能搞砸了他们关系的“连通性”方面。
在编写单元测试时,到目前为止,我对组件的了解如下:
describe('FilterSidebarComponent', () => {
let component: FilterSideBarComponent;
let fixture: ComponentFixture<FilterSideBarComponent>;
const sidebarServiceSpy = jasmine.createSpyObj('SidebarService', [
'showSidebar'
]);
const fb: FormBuilder = new FormBuilder();
configureTestSuite(() => {
TestBed.configureTestingModule({
imports: [
MockModule(FormsModule),
MockModule(ReactiveFormsModule),
MockModule(TranslateModule.forRoot())
],
declarations: [
FilterSideBarComponent,
CheckboxInputComponent,
MockComponent(CountrySingleSearchComponent),
MockComponent(UniversitiesSearcherComponent)
],
providers: [
{provide: SidebarService, useValue: sidebarServiceSpy},
{provide: FormBuilder, useValue: fb},
]
});
});
beforeEach(() => {
fixture = TestBed.createComponent(FilterSideBarComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
到目前为止,唯一编写的测试(“应该创建”)失败了,Jasmine 告诉我:TypeError: Cannot read property 'subscribe' of undefined at at FilterSideBarComponent.subscribe [as ngOnInit]
我在 StackOverflow 中搜索了关于模拟服务订阅的方法的类似主题,但我发现的所有示例都使用返回对象、数据或 Observables 的服务。
当然我可以重写服务,只要它保留它的功能,但是 - 有没有一种有效的方法来测试像我这样的“服务”,或者我应该以某种方式重写服务?
解决方案
问题是您订阅了未在模拟服务中定义的 toggleSidebar 属性。我不确定 Jasmine 是否支持模拟属性,所以我将创建一个类似于下面的模拟对象。当您想触发您的事件进行测试时,您将执行 sideBarSubject.next()。
另外我不认为建议在服务中使用 EventEmitters。我也会更改您的服务以使用主题。
如果您以后需要监视新的模拟侧边栏服务,您可以执行 spyOn(sidebarServiceSpy, 'showSideBar') ,这是必要的,因为您不再将其创建为间谍。
describe('FilterSidebarComponent', () => {
let component: FilterSideBarComponent;
let fixture: ComponentFixture<FilterSideBarComponent>;
let sideBarSubject = new Subject<void>();
const sidebarServiceSpy = {
toggleSidebar = sideBarSubject;
showSidebar: Function(){}
}
const fb: FormBuilder = new FormBuilder();
configureTestSuite(() => {
TestBed.configureTestingModule({
imports: [
MockModule(FormsModule),
MockModule(ReactiveFormsModule),
MockModule(TranslateModule.forRoot())
],
declarations: [
FilterSideBarComponent,
CheckboxInputComponent,
MockComponent(CountrySingleSearchComponent),
MockComponent(UniversitiesSearcherComponent)
],
providers: [
{provide: SidebarService, useValue: sidebarServiceSpy},
{provide: FormBuilder, useValue: fb},
]
});
});
beforeEach(() => {
fixture = TestBed.createComponent(FilterSideBarComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
推荐阅读
- eloquent - Laravel 5.7 firstOrNew 没有为新案例设置提供的值
- selenium - 为什么在 Selenium IDE 扩展中禁用“选项”?
- tensorflow - Tensorflow:计算 TFRecord 文件中的示例数——不使用已弃用的 `tf.python_io.tf_record_iterator`
- verilog - 为什么在我的单周期架构实现中输出没有改变/没有加载?
- javascript - 如何选择一个按钮并将其值显示到屏幕上
- database - 在 Oracle CDB 中创建的公共表空间可以跨所有 PDB 访问吗?
- servicestack - 在 ServiceStack API 中获取具有多个输入的请求
- junit - geode-junit 1.8.0 pom 依赖于不存在的 geode-old-versions 工件
- asp.net - web.config 重写规则在 blob 存储中的 Azure 静态网站中不起作用
- security - RFC 2986 和 RFC 4211 之间的区别,哪一个是证书签名请求的规范?