【问题标题】:Mocking Angular Material Dialog afterClosed() for unit test模拟 Angular Material Dialog afterClosed() 进行单元测试
【发布时间】:2019-05-07 20:46:39
【问题描述】:

我正在使用以下功能打开我的垫子对话框:

accept() {
  let dialogRef = this.dialog.open(AcceptDialogComponent, {
    data: {
      hasAccepted: false
    }
  })
  dialogRef.afterClosed().subscribe(result => {
    console.log(result);
    if (result.hasAccepted === true) {
      this.leadService.acceptLead(this.holdingAccountId, this.lead.id)
        .pipe(
          takeUntil(this.onDestroy$)
        )
        .subscribe(acceptLeadRes => {
            console.log(acceptLeadRes);
            this.leadService.updateLeadAction('accept');
          },
          (err: HttpErrorResponse) => {
            console.log(err);
            this.router.navigate(['/error']);
          });
    }
  });
}

我正在尝试为这个函数编写一个测试,它只是触发 afterClosed() 以便我可以检查是否调用了进行后端调用的服务方法。

component.spec.ts(在创建每个测试平台之前)

beforeEach(async (() => {
  TestBed.configureTestingModule({
      declarations: [LeadCardComponent, AcceptDialogComponent],
      imports: [
        requiredTestModules,
        JwtModule.forRoot({
          config: {
            tokenGetter: () => {
              return '';
            }
          }
        })
      ],
      providers: [
        ApplicationInsightsService,
        JwtHelperService,
        // { provide: LeadsService, useValue: leadServiceSpy }
      ],
    }),

    TestBed.overrideModule(BrowserDynamicTestingModule, {
      set: {
        entryComponents: [AcceptDialogComponent]
      }
    });
  TestBed.compileComponents();
}));

component.spec.ts(测试)

it('Return from AcceptLeadDialog with hasAccepted equals true should call acceptLead endpoint', () => {
  let matDiaglogref = dialog.open(AcceptDialogComponent, {
    data: {
      hasAccepted: false
    }
  });
  spyOn(matDiaglogref, 'afterClosed').and.callThrough().and.returnValue({
    hasAccepted: true
  });
  spyOn(leadService, 'acceptLead').and.callThrough();
  component.acceptLead();
  fixture.detectChanges();
  matDiaglogref.close();
  fixture.detectChanges();

  expect(leadService.acceptLead).toHaveBeenCalled();
});

测试当前失败,并显示“预期的间谍 acceptLead 已被调用”。我无法理解如何测试函数并执行某种模拟 MatDialogRef 以便我可以检查我的测试条件是否通过。

任何帮助/建议将不胜感激

更新从已接受答案实施的工作测试

it('Return from AcceptLeadDialog with hasAccepted equals true should call acceptLead endpoint', () => {
  spyOn(component.dialog, 'open')
    .and
    .returnValue({
      afterClosed: () => of({
        hasAccepted: true
      })
    });
  spyOn(leadService, 'acceptLead').and.callThrough();
  component.acceptLead();
  expect(component.dialog).toBeDefined();
  expect(leadService.acceptLead).toHaveBeenCalled();
});

【问题讨论】:

  • 你试过模拟MatDialogopen方法吗?这样做应该允许您返回一个您可以控制的模拟 MatDialogRef,并且您可以手动触发 afterClosed observable 以发出一个新值。
  • 我可以手动触发 afterClosed 并仍然测试它的响应逻辑是执行附加代码的条件吗?
  • 您的问题解决了吗?这是完整的解决方案,因为我还希望测试弹出窗口的打开和关闭
  • @dna 已经有一段时间了,但接受的答案对我的测试有效
  • 是否有可能进行完整的测试

标签: angular unit-testing jasmine angular-material karma-jasmine


【解决方案1】:

您可以通过这种方式测试 Angular Material Dialog 的 afterClosed 方法:

  1. import { of } from 'rxjs';
  2. 窥探对话框并为afterClosed()方法返回一个可观察对象
spyOn(component.dialog, 'open')
     .and
     .returnValue({afterClosed: () => of(true)});

基本上,dialogRef 的afterClosed() 期待一个 Observable。所以我们监视组件的对话框打开方法,并使用来自rxjsof 运算符为afterClosed() 方法返回一个Observable。

然后,您可以将returnValue 中的of(true) 替换为您在主组件对话框的afterClosed() 中发送的数据。

【讨论】:

  • 它正在抛出错误。你能帮我吗? '{ afterClosed: () => Observable; 类型的参数}' 不可分配给“MatDialogRef”类型的参数。输入 '{ afterClosed: () => Observable; }' 缺少来自“MatDialogRef”类型的以下属性:_overlayRef、_containerInstance、id、componentInstance 和 18 个以上。t
  • @SunilSoni,我面临同样的问题。你解决了吗?
  • @SunilSoni Well .open 通常应该返回一个MatDialogRef。在这种情况下,您只返回该对象的一小部分,因此 TypesScript 抱怨它。为了满足 TypeScript,您可以使用:.and.returnValue({ afterClosed: () => of(data) } as MatDialogRef<unknown>) 转换值。但请注意,TypeScript 的抱怨是非常有效的。如果在测试中调用MatDialogRef 的任何其他方法,尝试调用undefined 将失败。
  • @RodNolan 请参阅我对这个问题的其他评论。
  • 感谢@Elias 的帮助
【解决方案2】:

我在@Adithya Sreyaj 的第一篇文章中解决了这个问题,但添加了下一个更改:

spyOn(component.dialog, 'open')
    .and
    .returnValue({
        afterClosed: () => of(true)
    } as MatDialogRef<typeof component>);

【讨论】:

  • 这应该是第一名。
  • 虽然这只有在dialogcomponent 的公共成员时才有效,不是吗?
【解决方案3】:

我认为您错过了对组件进行单元测试的全部要点。据我了解:

  1. 你有一个函数accept()创建订阅this.dialog的关闭事件
  2. 您应该编写单元测试来验证创建订阅和调用服务的逻辑。
  3. 使dialogRef 成为component 的全局变量,而不是accept() 的私有变量。这将帮助您更好地测试代码。 private 在单元测试期间无法访问变量。

所以:

component.ts

accept() {
 this.dialogRef = this.dialog.open(AcceptDialogComponent, {
  data: {
    hasAccepted: false
      }
    })
 this.dialogRef.afterClosed().subscribe(result => {
  console.log(result);
  if (result.hasAccepted === true) {
    this.leadService.acceptLead(this.holdingAccountId, this.lead.id)
    .pipe(
      takeUntil(this.onDestroy$)
    )
    .subscribe (acceptLeadRes => {
      console.log(acceptLeadRes);
      this.leadService.updateLeadAction('accept');
    },
    (err: HttpErrorResponse) => {
      console.log(err);
      this.router.navigate(['/error']);
    });
   }
 });
}

spec.ts

it('should create subscription to Modal closing event and call "acceptLead()" of "leadService" ', () => {
    spyOn(component.dialogRef, 'afterClosed').and.returnValue(
        of({
            hasAccepted: false
        })
    );
    spyOn(component.leadService, 'acceptLead').and.callThrough();
    component.accept();
    expect(component.dialogRef).toBeDefined();
    expect(component.dialogRef.afterClosed).toHaveBeenCalled();
    expect(component.leadService.acceptLead).toHaveBeenCalled();
});


【讨论】:

  • 抱歉耽搁了。我无法让您的解决方案发挥作用。我可以将 dialogRef 设置为组件的全局变量,但我正在从该组件打开几个不同的对话框,这不会与能够做到这一点冲突吗?
  • @BrianStanley :如果您要打开多个dialogRef,则您必须在全球范围内为所有这些refs 创建单独的refs。如果您将变量保持为私有,您将无法使用 jasmine 对其进行测试
【解决方案4】:

有没有人使用对话框,传递一个回调函数,但不是在 afterClosed 中,回调是在 onOk 方法中?

例如

 let modalOptions = {
  uContent: 'are you sure you want to delete',
  uTitle: 'modal title',
  uOnOk: () => {
   callback function here
  }
};
this.modalService.create(modalOptions);

希望使用 jest 回调函数进行测试

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-10-04
    • 1970-01-01
    • 2016-03-22
    • 2018-09-04
    • 2013-11-12
    • 2018-11-21
    • 2018-10-19
    相关资源
    最近更新 更多