英文:
Testing a void async method in Angular, the spec is not waiting for the async calls to complete, which is making the test always fail
问题
以下是代码部分的翻译:
我在一个名为BurndownComponent的Angular组件中有一个名为loadIssues()的方法,它是void类型,并将值插入数组中。该方法使用了来自服务和组件本身的一些异步方法。在运行这些异步方法后,它填充数组。
我想为loadIssues()编写测试代码,但我不知道如何使测试等待数组填充。因为loadIssues是void类型,所以无法使用async await。我还尝试使用setTimeout,但它是异步运行的,不会等待loadIssues()的执行。
有人有关于如何编写这样的测试的想法吗?
代码部分翻译完毕。如果还有其他需要翻译的内容,请继续提供。
英文:
I have a method called loadIssues() in an Angular component called BurndownComponent that is void and inserts values into an array. This method uses some async methods from a service and from the component itself. After running those asyncs it fills in the array.
I want to write test code for loadIssues(), but I do not know how to make the test wait for the array to be filled. Because loadIssues is void I cannot use async await. I also tried using a setTimeout but it ran asynchronously and did not wait for the execution of loadIssues().
Does anyone have an idea on how I could write such a test?
The relevant codes are below:
loadIssues():
loadIssues(): void {
this.selectedMilestone = this.milestone;
this.issues = [];
console.log(this.selectedMilestone.reviewDate);
this.gitlabService.getIssues(this.selectedMilestone?.title).then(issues => {
let allIssues = issues.filter(i => [RequirementLabel.StoryTask, RequirementLabel.Task, RequirementLabel.Story].includes(i.requirement));
this.getIssueEvents(allIssues).then(issues => {
allIssues = issues;
console.log('allIssues ', allIssues.length);
// issues could be moved out of the milestone towards the end of it
// we consider a limit of 3 days before the review meeting date
if (new Date().getTime() >= this.selectedMilestone.reviewDate.getTime() - (3 * MILLISECONDS_PER_DAY)) {
this.getMilestoneRolledIssues().then(rolledIssues => {
const issuesIds = allIssues.map(i => i.id);
console.log(issuesIds);
allIssues = allIssues.concat(...rolledIssues.filter(i => !issuesIds.includes(i.id))); // removes duplicated issues
this.gitlabService.getDiscussions(allIssues).then(discussions => {
allIssues.forEach((issue, index) => issue.discussions = discussions[index]);
this.issues = allIssues;
});
});
}
else {
this.gitlabService.getDiscussions(allIssues).then(discussions => {
allIssues.forEach((issue, index) => issue.discussions = discussions[index]);
this.issues = allIssues;
});
}
});
});
Test attempt (BurndownComponent.spec.ts):
describe('BurndownComponent', () => {
let component: BurndownComponent;
let fixture: ComponentFixture<BurndownComponent>;
const data: object = jsonData;
let httpMock: object;
let stubGitLabService: GitlabService;
beforeEach(async () => {
httpMock = {
'get': (url, headers): Observable<object[]> => {
const endpoint = 'https://git.tecgraf.puc-rio.br/api/v4/';
const discussions = data['discussions-test'][0]['discussions']
.map(d => Discussion.getDiscussion(d));
const urlDiscussions = [
`${endpoint}projects/1710/issues/120/discussions`,
`${endpoint}projects/1710/issues/97/discussions`,
`${endpoint}projects/1210/issues/920/discussions`
];
if(urlDiscussions.includes(url)) {
return new Observable(subscriber => discussions[urlDiscussions.indexOf(url)]);
}
return new Observable(subscriber => null);
}
}
stubGitLabService = new GitlabService(<any> httpMock);
await TestBed.configureTestingModule({
declarations: [ BurndownComponent ],
providers: [
{ provide: GitlabService, useValue: stubGitLabService }
]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(BurndownComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('loads all issues - loadIssues()', async () => {
const milestoneData = Milestone.getMilestone(data['milestone-test'][0]);
const milestoneEventsData = data['all-milestone-events'][0]['events']
.map(me => MilestoneEvent.getMilestoneEvent(me));
const labelEventsData = data['label-events-burndown-component-test'][0]['events']
.map(le => LabelEvent.getLabelEvent(le));
const issues = data['issues-test-passed-milestone']
.map(i => Issue.getIssue(i));
const discussions = data['discussions-test'][0]['discussions']
.map(d => Discussion.getDiscussion(d));
issues.forEach((issue, index) => {
issue.labelEvents = labelEventsData.map(le => LabelEvent.copy(le));
issue.milestoneEvents = milestoneEventsData.map(me => MilestoneEvent.copy(me));
});
component.milestone = milestoneData;
stubGitLabService['getDiscussions'] = (issues: Issue[]): Promise<Discussion[][]> => {
return new Promise<Discussion[][]>(resolve => resolve(discussions))
};
const spyMilestoneRolledIssues = spyOn(component, 'getMilestoneRolledIssues')
.and
.returnValue(Promise.resolve(issues));
const spyIssueEvents = spyOn(component, 'getIssueEvents')
.and
.returnValue(Promise.resolve(issues));
const getDiscussionsSpy = spyOn(stubGitLabService, 'getDiscussions')
.and
.returnValue(new Promise(
resolve => {
console.log('discussions');
resolve(discussions)
}
));
await component.loadIssues();
expect(component.issues.length).toBe(3);
expect(spyMilestoneRolledIssues).toHaveBeenCalled();
expect(getDiscussionsSpy).toHaveBeenCalled();
});
答案1
得分: 0
你正在await
loadIssues
,但它不是一个异步函数。你可以通过返回then
返回的Promise
来解决这个问题,该Promise
将在适当的时间完成。
loadIssues() { // 删除显式的void返回类型
this.selectedMilestone = this.milestone;
this.issues = [];
console.log(this.selectedMilestone.reviewDate);
// 返回这个Promise
return this.gitlabService.getIssues(this.selectedMilestone?.title).then(issues => {
...
英文:
You are await
ing loadIssues
but it is not an async function. You can address this by returning the Promise
returned by then
which will complete at an appropriate time.
loadIssues() { // remove explicit void return type
this.selectedMilestone = this.milestone;
this.issues = [];
console.log(this.selectedMilestone.reviewDate);
// return the promise
return this.gitlabService.getIssues(this.selectedMilestone?.title).then(issues => {
...
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论