首页 > 解决方案 > 设置模拟服务并使用 jasmine/karma 和 Angular 调用它时出现问题

问题描述

我有一个调用另一个方法的方法。在该方法中,我订阅了一项服务,如果有匹配项,则使用 Angular 路由器服务进行导航。这是一个stackBlitz 示例

  goToMain(): void {
    this.viewFunc(this.queryString);
  }

  viewFunc(queryString): void {
    this.reportService.getAnotherReport(queryString).subscribe(x => {
      if (x.label.toLowerCase() === "accept & view") {
        const url = "main";
        this.router.navigate([url], {
          queryParams: { queryString: queryString }
        });
      }
    });
  }

我成功地在我的规范文件中创建了一个测试来测试该viewFunc方法是否被调用。然后,我尝试从下一个方法中调用的服务模拟订阅。我试图测试两件事 A. 订阅称为 B. 如果匹配则导航。

我收到错误“预期的间谍 getAnotherReport 已被调用。

  it("should call service", () => {
    const queryString = "10987";
    const testee = component;
    expect(testee).toBeTruthy();

    const routerSpy = { navigate: jasmine.createSpy("navigate") };
    const mySpy = jasmine.createSpy("getAnotherReport").and.callThrough();

    testee.viewFunc(queryString);

    expect(mySpy).toHaveBeenCalled();
    expect(routerSpy.navigate).toHaveBeenCalledWith(["/main"], {
      queryParams: { queryString: queryString }
    });
  });

这是一个stackBlitz 示例

我还不太了解 jasmine 和单元测试,我怀疑我为我的规范文件设置了错误的描述/测试平台。因此,如果有人可以提供帮助并查看stackBlitz 示例,我将不胜感激。

标签: angularunit-testingjasminekarma-jasmine

解决方案


工作 StackBlitz。


有几个错别字:

首先,我认为您的意思是声明getAnotherReport为值,而不是类型:

let getAnotherReport = {
  text: null,
  ref: "7478B45D4D4F1400223BD2F1",
  num: 19,
  urn: "123",
  title: "Test report",
  label: "accept & view"
};

然后,在SearchComponent.viewFunc你有url = 'main'和你的规范中,你有:

expect(routerSpy.navigate).toHaveBeenCalledWith(["/main"], {
  queryParams: { queryString: queryString }
});

它应该没有/

expect(routerSpy.navigate).toHaveBeenCalledWith(["main"], {
  queryParams: { queryString: queryString }
});

现在,让我们看看有趣的事情。

我们将首先探索第一个断言:

const mySpy = jasmine.createSpy("getAnotherReport").and.callThrough();
/* ... */
testee.viewFunc(queryString);
/* .... */
expect(mySpy).toHaveBeenCalled();

这里的问题与您决定如何进行间谍活动有关viewFunc。在这种情况下,您使用spyOn(component, "viewFunc");了:这些是您调用时在后台发生的相关位spyOn

var originalMethod = obj[methodName],
spiedMethod = createSpy(methodName, originalMethod),

wherecreateSpy将是一个间谍,即它仅用于跟踪函数被调用了多少次,使用哪些参数等。但它不会使用原始实现,这不是你想要的,因为你也有断言关注ReportService,它存在于viewFunc的原始实现中。

为了使用初始实现,您可以使用.and.callThrough()

// a quick look at the fact that it uses the original function
SpyStrategy.prototype.callThrough = function() {
  this.plan = this.originalFn;
  return this.getSpy();
};

reportService = TestBed.get(ReportService);
// and here we are using it
spyOn(component, "viewFunc").and.callThrough();

把它们放在一起:

// you can get ahold of the current spy like this
const mySpy = mockReportService.getAnotherReport.and.returnValue(
  of(getAnotherReport)
);

expect(testee).toBeTruthy();
testee.viewFunc(queryString);

expect(mySpy).toHaveBeenCalled();

现在进入第二个断言:

// inside providers array
{ provide: Router, useValue: routerSpy },


/* ... */

const routerSpy = { navigate: jasmine.createSpy("navigate") };

expect(routerSpy.navigate).toHaveBeenCalledWith(["main"], {
  queryParams: { queryString: queryString }
});

在这种情况下,请注意您使用的对象与您模拟类的对象不同Router

您必须使用与模拟相关类相同的对象,因此解决此问题的方法是:

let routerSpy = { navigate: jasmine.createSpy("navigate") };

/* ... */

// inside providers array
{ provide: Router, useValue: routerSpy },

/* ... */

const mySpy = mockReportService.getAnotherReport.and.returnValue(
      of(getAnotherReport)
    );

expect(testee).toBeTruthy();
testee.viewFunc(queryString);

expect(mySpy).toHaveBeenCalled();
expect(routerSpy.navigate).toHaveBeenCalledWith(["main"], {
  queryParams: { queryString: queryString }
});

小建议

我对 Jasmine 也不是很熟悉,但它帮助我了解了它在引擎盖下的作用。无需了解每一个微小细节,您至少可以直观地了解如何解决问题。因此,当在 StackBlitz 应用程序中时,例如您链接的应用程序,您可以:

  1. 打开开发工具
  2. 按下CTRL + P并输入jasmine.js(可能有多个文件,正确的文件是其中包含已知方法的文件,例如callThrough- 您可以通过按下CTRL + SHIFT + O并输入方法名称来检查)

此外,您可以在那里搜索search.component.ts并放置一些断点,以获得更流畅的调试体验。


推荐阅读