【问题标题】:How to inject another service to specific test case in angular testing如何在角度测试中向特定测试用例注入另一个服务
【发布时间】:2017-08-24 07:55:16
【问题描述】:

正如上面的标题问题 - 是否可以将另一个服务实例注入特定的测试功能?我的意思是,在测试服中,我在 beforeEach 方法中注入了一些服务。

创建存根:

let userServiceStub = {
  hasReadPermissions() { return true; },
  isUserInformationAvailable() { return true; },
  isUserRequestForbidden() { return true; }
};

在beforeEach方法中配置测试模块:

TestBed.configureTestingModule({
  declarations: [
    SomeComponent
  ],
  providers: [{ provide: UserService, useValue: userServiceStub },
              { provide: AnotherService, useValue: anotherServiceStub }],
  schemas: [ NO_ERRORS_SCHEMA ]
}).compileComponents();

然后我要测试用例:

首先我想使用在 TestBed 中注入的服务存根

it('should render topbar when user has read permissions', () => {
  let topbar = debugElement.query(By.css('topbar'));
  expect(topbar).toBeTruthy();
});

我想使用同一服务的另一个存根的第二个地方:

let anotherUserServiceStub = {
  hasReadPermissions() { return false; },
  isUserInformationAvailable() { return false; },
  isUserRequestForbidden() { return true; }
};

it('should render appropriate message when user has not read permissions', 
   inject([UserService], (userService: UserService) => { <--- how to inject another user service stub here?
       let message = debugElement.query(By.css('h2'));
       expect(message).toBe('you have no permissions');
   }));

或者也许我试图以不恰当的方式做这件事?请指出正确的方法。

[[编辑]]

部分组件逻辑:

dataUnavailable: boolean = false;
noPermissions: boolean = false;

constructor(private toastr: ToastsManager, 
          vRef: ViewContainerRef,
          private userService: UserService,
          private router: Router) {
  this.toastr.setRootViewContainerRef(vRef);
}

ngOnInit(): void {
  if (!this.isUserInformationAvailable()) {
    if (this.isUserRequestForbidden()) {
      this.noPermissions = true;
    } else {
      this.dataUnavailable = true;
      this.toastr.error("An error occured while getting data from server.");
    }
  }
}

hasUserReadPermissions(): boolean {
  return this.userService.hasReadPermissions();
}

模板:

<ng-container *ngIf="hasUserReadPermissions()">
  <topbar></topbar>
  <main-menu></main-menu>
  <router-outlet></router-outlet>
</ng-container>
<div class="row" *ngIf="dataUnavailable">
  <div class="col-sm-12">
    <h2 class="text-center rcm-bold-message">Server data is not available.</h2>
  </div>
</div>
<div class="row" *ngIf="noPermissions">
  <div class="col-sm-12">
    <h2 class="text-center rcm-bold-message">You do not have sufficient permissions.</h2>
  </div>
</div>

【问题讨论】:

  • Mhhhhh,你为什么要这样做?服务应该是Singleton,所以你不想在你的应用程序中使用不同的实例,所以没有理由像这样测试它。顺便说一句,如果您覆盖服务,请使用 useClass 而不是 useValue,因为 useClass 将确保解决依赖关系。
  • 我想这样做以向不同的测试用例提供另一种服务方法结果。尝试测试组件行为,以防用户具有读取权限(hasReadPermissions() { return true; })而没有读取权限(hasReadPermissions() { return false; })。有没有更好的解决方案?
  • 啊,好吧,现在我明白了;)好吧,基本上你在这个测试中测试你的组件,对吧?所以你为它存根你的服务,因为实际的服务在这个测试中并不重要,但它仍然会被使用。但这并不一定意味着您必须使用存根服务的返回值。例如,如果您在您正在测试的 h2 元素上有一个 *ngIf,并且这个 *ngIf* depends on the return value of your service method, just ignore that fact and mock the property which is used in your *ngIf 直接。希望我很清楚。
  • 如果您将要测试的组件模板部分添加到您的问题中,如果您愿意,我可以通过答案更好地解释它。
  • 好的,谢谢。被测组件已添加到帖子中。

标签: angular unit-testing testing


【解决方案1】:

好的,正如我在对您的问题的评论中所说,您基本上可以在组件测试中忽略服务的实际响应(因为您的服务方法的结果无论如何都应该单独测试)。

如果您想更改返回值,以便不必保留这些存根/服务的不同存根或实例,您可以使用jasmine.Spy 监视此方法并使用提供的方法@987654322 @ 定义将在您的测试中返回的值:

let spy: jasmine.Spy = spyOn(comp.userService, 'hasUserReadPermissions').and.returnValue(false);

let message = debugElement.query(By.css('h2'));
expect(message).toBe('you have no permissions');

关于您的组件的快速提示:您正在使用*ngIf 中的方法。没关系,大多数时候,但请记住,这个函数将在 angulars 生命周期钩子的每个摘要循环中调用(意思是 ngOnInitngAfterViewInitngDoCheck 等等)。这将导致您的函数在一秒钟内被多次调用。不要使用有些“慢”的方法(我知道,慢是相对的)。在组件内调用该函数一次,保留对返回值的引用,就像对 dataUnavailable 所做的那样。

希望对你有帮助。


完成更新:

有时,你不想在测试中模拟一个方法,尽管它被调用了,但要阻止调用它的实际实现(可能是因为它做了你不关心的事情about 在您的测试中,或者它具有依赖项或数千个嵌套的 observables 或简单的超时,这对测试来说是一团糟)。在这种情况下,您实际上可以重写该函数:

  comp.userService.hasUserReadPermissions = function() { return false; };
  // do some stuff so the function is called
  expect(...).toBe(...);

看起来很丑,但有时它确实安全。

【讨论】:

    【解决方案2】:

    首先,您提供的有关您如何在组件或服务中设置用户的信息较少?

    注入后,您可以使用 spyOn 并返回您的用户。

    spyOn(userService, 'getUser').and.returnValue(yourUser);

    【讨论】:

    • 用户已设置并保持服务状态。请注意编辑中的代码部分。
    • 啊,看看上面的答案。我在你编辑之前发了这个帖子。另请注意,上面的帖子说 false = false 这并不是真正的测试。尝试添加用户,然后 userService 的方法应该自动说 hasUserReadPermissions = true 或 false。
    【解决方案3】:

    这个解决方案对我有用,希望它也对你有用

     it('should render appropriate message when user has not read permissions', 
         inject([UserService, AnotherService], (userServiceStub: UserService, 
                anotherServiceStub: AnotherService ) => {
                let message = debugElement.query(By.css('h2'));
                expect(message).toBe('you have no permissions');
           }));
    

    我刚刚添加了另一个服务,用逗号分隔。

    【讨论】:

      猜你喜欢
      • 2019-04-19
      • 2019-03-28
      • 2021-07-09
      • 1970-01-01
      • 2020-01-26
      • 2017-06-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多