英文:
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 awaiting 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 => {
...
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论