如何在angular测试中为特定的测试用例注入其他服务

How to inject another service to specific test case in angular testing

与上述标题问题一样-是否可以将服务的另一个实例注入特定的测试功能?我的意思是这样的情况:在测试服中,我在beforeEach方法中注入了一些服务。

创建Stubbing:

1
2
3
4
5
let userServiceStub = {
  hasReadPermissions() { return true; },
  isUserInformationAvailable() { return true; },
  isUserRequestForbidden() { return true; }
};

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

1
2
3
4
5
6
7
8
TestBed.configureTestingModule({
  declarations: [
    SomeComponent
  ],
  providers: [{ provide: UserService, useValue: userServiceStub },
              { provide: AnotherService, useValue: anotherServiceStub }],
  schemas: [ NO_ERRORS_SCHEMA ]
}).compileComponents();

然后我要测试案例:

首先要在TestBed

中使用注入的服务Stubbing

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

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

1
2
3
4
5
6
7
8
9
10
11
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');
   }));

或者也许我正在尝试以所有不合适的方式进行操作?请指出正确的方法。

[[编辑]]

组件逻辑的一部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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();
}

模板:

1
2
3
4
5
6
7
8
9
10
11
12
13
<ng-container *ngIf="hasUserReadPermissions()">
  <topbar></topbar>
  <main-menu></main-menu>
  <router-outlet></router-outlet>
</ng-container>

 
    <h2 class="text-center rcm-bold-message">Server data is not available.
 


 
    <h2 class="text-center rcm-bold-message">You do not have sufficient permissions.


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

如果要更改返回值,而不必保留其他Stubbing或这些Stubbing/服务的实例,则可以使用jasmine.Spy监视此方法,并使用提供的方法returnValue定义将在测试中返回的值:

1
2
3
4
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中使用一种方法。在大多数情况下都可以,但是请记住,将在每个angular生命周期挂钩的摘要循环中调用此函数(意味着在ngOnInitngAfterViewInitngDoCheck等上)。这将导致您的函数在一秒钟内被多次调用。不要使用有些"慢"的方法(我知道,慢是相对的)。在组件内部调用一次函数,保留对返回值的引用,就像使用dataUnavailable一样。

希望有帮助。

完成更新:

有时,尽管调用了该方法,但您仍不想在测试中模拟该方法,但可以通过其实际实现阻止对该方法的调用(也许是因为它确实执行了您在测试中不需要关心的事情,或它具有依赖项或成千上万个嵌套的可观察变量或简单的超时,这很麻烦进行测试)。在这种情况下,您实际上可以覆盖该函数:

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

看上去很丑,但有时却可以过安全的生活。


此解决方案对我有用,希望它对您也有用

1
2
3
4
5
6
 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');
       }));

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


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

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

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