【问题标题】:Problems setting up mock services and calling it with jasmine/ karma and Angular设置模拟服务并使用 jasmine/karma 和 Angular 调用它时出现问题
【发布时间】:2021-05-23 20:30:49
【问题描述】:

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

  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. 如果匹配则导航。

我收到错误消息“预期 spy 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 example

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

【问题讨论】:

    标签: angular unit-testing jasmine karma-jasmine


    【解决方案1】:

    Working 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),
    

    其中createSpy 将是一个 spy,即它仅用于跟踪函数被调用的次数、使用的参数等。但它不会使用原始实现,这不是您想要的,因为您还有与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 并在其中放置一些断点以获得更流畅的调试体验。

    【讨论】:

    • 多么棒的答案!多年来我在这里看到的最佳答案。感谢并希望其他人可以使用它。
    • 谢谢!很高兴能提供帮助!
    • 我收到 TypeError 的任何原因:this.mockReportService.getAnotherReport.subscribe 不是我本地环境中的函数?据我所知,设置与 stackblitz 相同。第 80 行
    • @TomRudge 可能是因为mockReportService.getAnotherReport 属于“间谍”类型。尝试控制台记录它。还要检查您是否安装了必要的@types/**
    猜你喜欢
    • 2015-12-03
    • 1970-01-01
    • 2014-12-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-15
    • 2021-11-05
    相关资源
    最近更新 更多