angular - 如何在 Angular Testing 中向特定测试用例注入(inject)另一个服务

标签 angular unit-testing testing

如上面的标题问题所示 - 是否可以将服务的另一个实例注入(inject)到特定的测试函数中?我的意思是,在测试套件中我在 beforeEach 方法中注入(inject)一些服务的情况。

创建 stub :

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 中使用注入(inject)的服务 stub

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

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

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>

最佳答案

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

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

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 生命周期钩子(Hook)的每个摘要循环中调用(意味着在 ngOnInitngAfterViewInit 上) >、ngDoCheck 等)。这将导致您的函数在一秒钟内被多次调用。不要使用有些“慢”的方法(我知道,慢是相对的)。在组件内部调用该函数后,保留对返回值的引用,就像对 dataUnavailable 所做的那样。

希望有帮助。


完成更新:

有时,尽管调用了某个方法,但您不想在测试中模拟该方法,而是阻止通过其实际实现来调用它(可能是因为它做了您不关心的事情)在你的测试中,或者它有依赖项或数千个嵌套的可观察值或简单的超时,那么测试会很困惑)。在这种情况下,您实际上可以重写该函数:

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

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

关于angular - 如何在 Angular Testing 中向特定测试用例注入(inject)另一个服务,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45855982/

相关文章:

javascript - Angular 服务和 HttpClient 类型不存在

javascript - 以 Angular 进行单元测试文件上传

java - 比较两个对象时 JUnit assertEquals() 不起作用

javascript - Chai:如何使用 'should' 语法测试未定义

angular - 从 Angular 的下拉列表中获取选定的值

Angular - 创建通用装饰器包装@HostListener

angular - 使用 angular2 中的组件交互通过 <router-outlet> 传递数据

ios - 在项目中包含Metal库的同时如何在Xcode中进行单元测试

c# - 如何在解决方案中集成测试控制台应用程序?

iphone - 在真实的 iPhone 设备上进行旋转应用测试