Angular Jest 测试打开 MatDialog 的组件 - open 不是函数

rmc*_*rry 3 unit-testing mocking jestjs angular-material angular

与这个问题类似,但它没有提供对我有用的答案。

我有一个简单的组件,它有一个打开对话框的方法:

  enterGiveaway() {
    this.dialog.open(SpendTicketsDialogComponent, {
      width: '370px',
      height: '600px'
    });
  }
Run Code Online (Sandbox Code Playgroud)

现在我只想测试调用该方法是否会导致打开对话框。

测试失败并出现以下错误:

  expect(spy).toBeCalledTimes(expected)

    Expected number of calls: 1
    Received number of calls: 0
Run Code Online (Sandbox Code Playgroud)

用这个代码:

  enterGiveaway() {
    this.dialog.open(SpendTicketsDialogComponent, {
      width: '370px',
      height: '600px'
    });
  }
Run Code Online (Sandbox Code Playgroud)

我当然知道,MatDialog 并未引用实际SpendTicketsDialogComponent打开的对话框。所以我尝试为对话框提供一个模拟对象:

  expect(spy).toBeCalledTimes(expected)

    Expected number of calls: 1
    Received number of calls: 0
Run Code Online (Sandbox Code Playgroud)

但这会引发错误this.dialog.open is not a function

实际上,我认为这两种解决方案都不正确,因为我需要检查调用 EnterGiveaway 是否打开 SpendTicketsDialog。

那么我该如何验证呢?

yur*_*zui 7

你的模拟MatDialog还不够好。

providers: [{provide: MatDialog, useValue: dialogMock}],
Run Code Online (Sandbox Code Playgroud)

既然dialogMock是一个类,那么你应该像这样使用它:

useValue: new dialogMock()
Run Code Online (Sandbox Code Playgroud)

或者

useClass: dialogMock
Run Code Online (Sandbox Code Playgroud)

否则你的模拟将没有open方法。

提示始终使用大写字母命名您的课程

现在让我们转到您的测试用例并注意执行顺序

component.enterGiveaway();   <-------------------------  (1)         
...
const spy = spyOn(dialog, 'open').and.callThrough(); <-- (2)

expect(spy).toBeCalledTimes(1); <----------------------- (3)
Run Code Online (Sandbox Code Playgroud)
  • (1)modal.open()方法正在执行的地方。如果我们为对话框提供了正确的模拟,那么它将毫无问题地执行

  • (2)是您监视对话框方法以计算调用时间的地方。以后就不会再叫这个间谍了。modal.open() 已在步骤中执行 (1)

  • (3)您的测试失败,因为由于步骤中描述的原因没有任何对您的间谍的呼叫(2)

解决方案非常不言自明:在执行enterGiveaway()方法之前放置间谍程序:

  const spy = spyOn(dialog, 'open').and.callThrough();
  component.enterGiveaway();
Run Code Online (Sandbox Code Playgroud)

茉莉花.createSpyObj

还有另一种方便的方法可以使用 来模拟您的MatDialogand方法。openjasmine.createSpyObj

let dialog: jasmine.SpyObj<MatDialog>;
...

{provide: MatDialog, useValue: jasmine.createSpyObj<MatDialog>(['open'])}

beforeEach(() => {
  ...
  dialog = TestBed.inject(MatDialog) as jasmine.SpyObj<MatDialog>;
  ...
});

it('should open the spend tickets dialog', async(() => {
  component.enterGiveaway();

  expect(dialog.open.calls.count()).toBe(1);
}));
Run Code Online (Sandbox Code Playgroud)