我正在尝试进行组件集成测试(即包括 Angular 生命周期事件),但我很难嘲笑 Jasmine 的服务,一个公开Subscribable
的服务。
重现我的方案:
ng new ng-comp-tests
ng generate component my-screen
ng generate service crud
ng generate class item
将类更新为:
export class Item {
constructor(public name: string) { }
}
然后使服务如下所示:
export class CrudService {
private thing = new Subject<Item>();
constructor() {
setInterval(() => { this.thing.next(new Item('Thing ' + Math.random())); }, 1000);
}
getThingObservable() {
return this.thing.asObservable();
}
}
将组件更改为如下所示(并将其作为应用程序组件的主要内容包含在内):
<p *ngIf="!!thing">{{thing.name}}</p>
export class MyScreenComponent implements OnInit, OnDestroy {
public thing: Item;
private thingSubscription: Subscription;
constructor(private service: CrudService) { }
ngOnInit() {
this.thingSubscription = this.service
.getThingObservable()
.subscribe(t => { this.thing = t; });
}
ngOnDestroy(): void {
this.thingSubscription.unsubscribe();
}
}
最后,将规范更改为:
describe('MyScreenComponent', () => {
let component: MyScreenComponent;
let fixture: ComponentFixture<MyScreenComponent>;
beforeEach(async(() => {
const serviceSpy = jasmine.createSpyObj('CrudService', {
getThingObservable: () => new Subject<Item>().asObservable()
});
TestBed.configureTestingModule({
declarations: [ MyScreenComponent ],
providers: [ { provide: CrudService, useValue: serviceSpy } ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(MyScreenComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
这将为测试提供例外:
MyScreenComponent 应该创建
[object ErrorEvent] thrown
该异常并没有真正的帮助,Chrome 的控制台也没有帮助,因为它显示了以下缩写日志:
- "无法加载 ng:///DynamicTestModule/...ngfactory.js:跨源请求...">
- "无法加载 ng:///DynamicTestModule/...ngfactory.js:跨源请求...">
- "未捕获的穹顶:无法执行'发送'....">
- "清理组件时出错...无法读取未定义的属性"取消订阅"...">
相关问题的最佳答案表明我忘记做fixture.detectChanges()
(这可以防止ngOnInit
触发),但正如你所看到的,我的代码中确实有这个。
这里一定还有其他问题,但是什么?
答案就在jasmine.createSpyObj(...)
文档中,说明了第二个参数(强调我的):
要为其创建间谍的方法名称数组,或Object,其键将是方法名称和值 returnValue。
在你的代码中,你为getThingObservable
提供了一个虚假的实现,如下所示:
getThingObservable: () => new Subject<Item>().asObservable()
这样,getThingObservable
间谍将在组件调用它时返回一个 lambda,该 lambda没有组件尝试执行的subscribe
,因此ngOnInit
完全失败。ngOnDestroy
仅作为次要效果失败。
相反,您应该直接传递 returnValue,如下所示:
getThingObservable: new Subject<Item>().asObservable()
然后,当组件调用getThingObservable
时,它将获得实际的可观察量,并且事情会很好。
附言。您也可以简化为使用{ of } from 'rxjs/observable/of'
:
getThingObservable: of<Item>()
最后,您还可以求助于大理石测试,以更好地控制测试中的可观察量。