【发布时间】:2017-09-04 12:49:33
【问题描述】:
上下文
我有一个组件。在其中,ngOnInit 函数调用组件的另一个函数来检索用户列表。我想做两个系列的tets:
- 首先测试 ngOnInit 是否正确触发并填充用户列表
- 第二次我想测试我的刷新函数,它也调用 getUserList()
当我调用 fixture.detectChanges() 时,使用 ngOnInit 触发器的第一个测试工作正常。
问题
我的问题是在测试刷新函数时:一旦我调用了 fixture.detectChanges(),ngOnInit 就会被触发,然后我无法知道我的结果来自哪里以及我的 refresh() 函数是否会被正确测试。
在我对refresh() 方法进行第二系列测试之前,有什么办法可以“删除”或“阻止”ngOnInit(),这样就不会在fixture.detectChanges() 上调用它? p>
我试图查看overrideComponent,但它似乎不允许删除ngOnInit()。
或者在我的情况下,除了使用fixture.detectChanges 之外,还有什么方法可以检测到更改?
代码
这是组件、存根服务和我的规范文件的代码。
组件
import { Component, OnInit, ViewContainerRef } from '@angular/core';
import { UserManagementService } from '../../shared/services/global.api';
import { UserListItemComponent } from './user-list-item.component';
@Component({
selector: 'app-user-list',
templateUrl: './user-list.component.html'
})
export class UserListComponent implements OnInit {
public userList = [];
constructor(
private _userManagementService: UserManagementService,
) { }
ngOnInit() {
this.getUserList();
}
onRefreshUserList() {
this.getUserList();
}
getUserList(notifyWhenComplete = false) {
this._userManagementService.getListUsers().subscribe(
result => {
this.userList = result.objects;
},
error => {
console.error(error);
},
() => {
if (notifyWhenComplete) {
console.info('Notification');
}
}
);
}
}
组件规格文件
import { NO_ERRORS_SCHEMA } from '@angular/core';
import {
async,
fakeAsync,
ComponentFixture,
TestBed,
tick,
inject
} from '@angular/core/testing';
import { Observable } from 'rxjs/Observable';
// Components
import { UserListComponent } from './user-list.component';
// Services
import { UserManagementService } from '../../shared/services/global.api';
import { UserManagementServiceStub } from '../../testing/services/global.api.stub';
let comp: UserListComponent;
let fixture: ComponentFixture<UserListComponent>;
let service: UserManagementService;
describe('UserListComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [UserListComponent],
imports: [],
providers: [
{
provide: UserManagementService,
useClass: UserManagementServiceStub
}
],
schemas: [ NO_ERRORS_SCHEMA ]
})
.compileComponents();
}));
tests();
});
function tests() {
beforeEach(() => {
fixture = TestBed.createComponent(UserListComponent);
comp = fixture.componentInstance;
service = TestBed.get(UserManagementService);
});
it(`should be initialized`, () => {
expect(fixture).toBeDefined();
expect(comp).toBeDefined();
});
it(`should NOT have any user in list before ngOnInit`, () => {
expect(comp.userList.length).toBe(0, 'user list is empty before init');
});
it(`should get the user List after ngOnInit`, async(() => {
fixture.detectChanges(); // This triggers the ngOnInit and thus the getUserList() method
// Works perfectly. ngOnInit was triggered and my list is OK
expect(comp.userList.length).toBe(3, 'user list exists after init');
}));
it(`should get the user List via refresh function`, fakeAsync(() => {
comp.onRefreshUserList(); // Can be commented, the test will pass because of ngOnInit trigger
tick();
// This triggers the ngOnInit which ALSO call getUserList()
// so my result can come from getUserList() method called from both source: onRefreshUserList() AND through ngOnInit().
fixture.detectChanges();
// If I comment the first line, the expectation is met because ngOnInit was triggered!
expect(comp.userList.length).toBe(3, 'user list after function call');
}));
}
存根服务(如果需要)
import { Observable } from 'rxjs/Observable';
export class UserManagementServiceStub {
getListUsers() {
return Observable.from([
{
count: 3,
objects:
[
{
id: "7f5a6610-f59b-4cd7-b649-1ea3cf72347f",
name: "user 1",
group: "any"
},
{
id: "d6f54c29-810e-43d8-8083-0712d1c412a3",
name: "user 2",
group: "any"
},
{
id: "2874f506-009a-4af8-8ca5-f6e6ba1824cb",
name: "user 3",
group: "any"
}
]
}
]);
}
}
我的试炼
我尝试了一些“解决方法”,但我发现它有点......冗长而且可能有点矫枉过正!
例如:
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,因为一旦你创建了一个组件实例,它就会被触发,你需要创建一个组件实例来编写测试用例
-
最好有一个更可控的存根;这样您就可以控制每次调用它时返回的数据,以便您知道第二次数据应该不同。您可以使用
Subject允许您在本地或在存根上使用其他测试方法为订阅者推送新数据,或者监视.and.returnValue方法,无论您喜欢什么。 -
我对 spy 感到很不舒服,但在我的两个系列测试中注入不同的 returnValue 似乎是一个很好的解决方案,也许可以通过在每个 beforeEach 中设置两个不同的 spy。你有任何关于如何实现这一目标的例子吗?
标签: angular typescript jasmine karma-jasmine