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

时间:2017-08-24 07:55:16

标签: angular unit-testing testing

与上述标题问题一样 - 是否可以将另一个服务实例注入特定的测试功能?我的意思是情况,在测试套装中我在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>

3 个答案:

答案 0 :(得分:1)

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

如果要更改返回值,以便不必保留这些存根/服务的不同存根或实例,可以使用class ObjectSerializer(serializers.HyperlinkedModelSerializer): state = serializers.SerializerMethodField() class Meta: model = models.Object fields = ('state') def get_state(self, obj): state = obj.state(self.context['dt']) state_serializer = StateSerializer(state) return state_serializer.data 监视此方法并使用提供的方法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'); 中使用了一种方法。没关系,大部分时间,但请记住,这个函数将在角度生命周期钩子的每个摘要圈中调用(意思是*ngIfngOnInit,{{1} } 等等)。这将导致您的函数在一秒钟内被多次调用。不要使用有些“慢”的方法(我知道,慢是相对的)。在组件内部调用一次函数,像对ngAfterViewInit一样保留对返回值的引用。

希望它有所帮助。

完成更新:

有时,你不想在测试中模拟一个方法,尽管它被调用了,但是阻止用它的实际实现来调用它(也许是因为它确实你不关心它关于你的测试,或它有依赖关系或成千上万的嵌套observables或简单的超时,这是一个混乱的测试然后)。在这种情况下,您实际上可以覆盖函数:

ngDoCheck

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

答案 1 :(得分:0)

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

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

spyOn(userService,&#39; getUser&#39;)。and.returnValue(yourUser);

答案 2 :(得分:0)

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

const { Switch, Route, BrowserRouter } = window.ReactRouterDOM;

ReactDOM.render(<BrowserRouter>
    <Switch>
        <Route path='/' component={HomePage} exact/>
        <Route path='/history' component={HistoryPage} exact/>
        <Route path='/login' component={LoginPage} exact/>
        <Route component={Error404}/>
    </Switch>
</BrowserRouter>,document.getElementById('root'));

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