Angular 角度测试如何防止ngOnInit调用直接测试方法 上下文
我有一个组件。在它内部,ngOnInit函数调用组件的另一个函数来检索用户列表。我想制作两个系列的TET:Angular 角度测试如何防止ngOnInit调用直接测试方法 上下文,angular,typescript,jasmine,karma-jasmine,Angular,Typescript,Jasmine,Karma Jasmine,我有一个组件。在它内部,ngOnInit函数调用组件的另一个函数来检索用户列表。我想制作两个系列的TET: 首先测试Ngonit是否正确触发并填充用户列表 第二次,我想测试我的刷新函数,它也调用getUserList() 当我调用fixture.detectChanges()时,第一个测试是使用ngOnInit触发器进行的 问题 我的问题是在测试refresh函数时:只要我调用fixture.detectChanges(),就会触发ngOnInit,然后我就无法知道我的结果来自何处,也无法知
- 首先测试Ngonit是否正确触发并填充用户列表
- 第二次,我想测试我的刷新函数,它也调用getUserList()
refresh()
方法进行第二系列测试之前,有没有办法“删除”或“阻止”ngOnInit()
,这样就不会在fixture.detectChanges()上调用它?
我试图查看overrideComponent
,但它似乎不允许删除ngOnInit()
或者在我的情况下,除了使用fixture.detectChanges
之外,还有其他方法检测更改吗?
代码
下面是组件、存根服务和我的规范文件的代码
组成部分
元件规格文件
我的审判
我尝试了一些“变通方法”,但我发现有点。。。。长篇大论,也许是矫枉过正
例如:
it(`should get the user List via refresh function`, fakeAsync(() => {
expect(comp.userList.length).toBe(0, 'user list must be empty');
// Here ngOnInit is called, so I override the result from onInit
fixture.detectChanges();
expect(comp.userList.length).toBe(3, 'ngOnInit');
comp.userList = [];
fixture.detectChanges();
expect(comp.userList.length).toBe(0, 'ngOnInit');
// Then call the refresh function
comp.onRefreshUserList(true);
tick();
fixture.detectChanges();
expect(comp.userList.length).toBe(3, 'user list after function call');
}));
防止调用生命周期挂钩(ngOnInit
)是一个错误的方向。这个问题有两个可能的原因。要么测试不够孤立,要么测试策略错误
角导轨相当:
但是,使用独立的单元测试来探索应用程序类的内部逻辑通常会更有效,因为这些单元测试不依赖于角度。此类测试通常更小,更易于阅读、编写和维护
所以独立测试应该实例化一个类并测试它的方法
userManagementService = new UserManagementServiceStub;
comp = new UserListComponent(userManagementService);
spyOn(comp, 'getUserList');
...
comp.ngOnInit();
expect(comp.getUserList).toHaveBeenCalled();
...
comp.onRefreshUserList();
expect(comp.getUserList).toHaveBeenCalled();
独立测试有一个缺点——它们不测试DI,而测试台测试则测试DI。根据观点和测试策略,独立测试可以被视为单元测试,而测试台测试可以被视为功能测试。一个好的测试套件可以同时包含这两者
在上面的代码中,应该通过刷新函数获取用户列表
测试显然是一个功能测试,它将组件实例视为黑盒
可以添加两个测试台单元测试来填补这一空白,它们可能足够可靠,不需要进行单独的测试(尽管后者肯定更精确):
我个人更喜欢在每次测试中取消组件
beforeEach(() => {
UserListComponent.prototype.ngOnInit = () => {} ;
....
});
你不能阻止Ngonit,因为当你创建一个组件实例时,它就被触发了,你需要创建一个组件实例来编写测试用例。最好有一个更可控的存根;这样,您就可以控制每次调用它时返回的数据,这样您就知道第二次调用时数据应该是不同的。您可以使用
主题
来为订阅者推送新数据,可以本地推送,也可以在存根上使用其他测试方法,或者监视方法。和.returnValue
随您喜欢。我对spy很不舒服,但在我的两个系列测试中插入不同的returnValue似乎是一个很好的解决方案,也许先把间谍分为两类。你有没有关于如何做到这一点的例子?
it(`should get the user List via refresh function`, fakeAsync(() => {
expect(comp.userList.length).toBe(0, 'user list must be empty');
// Here ngOnInit is called, so I override the result from onInit
fixture.detectChanges();
expect(comp.userList.length).toBe(3, 'ngOnInit');
comp.userList = [];
fixture.detectChanges();
expect(comp.userList.length).toBe(0, 'ngOnInit');
// Then call the refresh function
comp.onRefreshUserList(true);
tick();
fixture.detectChanges();
expect(comp.userList.length).toBe(3, 'user list after function call');
}));
it(`should get the user List via refresh function`, fakeAsync(() => {
let ngOnInitFn = UserListComponent.prototype.ngOnInit;
UserListComponent.prototype.ngOnInit = () => {} // override ngOnInit
comp.onRefreshUserList();
tick();
fixture.detectChanges();
UserListComponent.prototype.ngOnInit = ngOnInitFn; // revert ngOnInit
expect(comp.userList.length).toBe(3, 'user list after function call');
}));
userManagementService = new UserManagementServiceStub;
comp = new UserListComponent(userManagementService);
spyOn(comp, 'getUserList');
...
comp.ngOnInit();
expect(comp.getUserList).toHaveBeenCalled();
...
comp.onRefreshUserList();
expect(comp.getUserList).toHaveBeenCalled();
spyOn(comp, 'getUserList');
comp.onRefreshUserList();
expect(comp.getUserList).toHaveBeenCalledTimes(1);
...
spyOn(comp, 'getUserList');
spyOn(comp, 'ngOnInit').and.callThrough();
tick();
fixture.detectChanges();
expect(comp.ngOnInit).toHaveBeenCalled();
expect(comp.getUserList).toHaveBeenCalledTimes(1);
beforeEach(() => {
UserListComponent.prototype.ngOnInit = () => {} ;
....
});