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

huangapple go评论66阅读模式
英文:

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 => {
   ...

huangapple
  • 本文由 发表于 2023年2月14日 22:13:59
  • 转载请务必保留本文链接:https://go.coder-hub.com/75449089.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定