Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/angular/29.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Jasmine 在Angular 2 ngOnInit中测试承诺_Jasmine_Angular - Fatal编程技术网

Jasmine 在Angular 2 ngOnInit中测试承诺

Jasmine 在Angular 2 ngOnInit中测试承诺,jasmine,angular,Jasmine,Angular,我有一个角度2组件,我正试图进行测试,但我遇到了问题,因为数据是在ngOnInit函数中设置的,因此无法立即在单元测试中使用 user-view.component.ts: import {Component, OnInit} from 'angular2/core'; import {RouteParams} from 'angular2/router'; import {User} from './user'; import {UserService} from './user.servi

我有一个角度2组件,我正试图进行测试,但我遇到了问题,因为数据是在
ngOnInit
函数中设置的,因此无法立即在单元测试中使用

user-view.component.ts:

import {Component, OnInit} from 'angular2/core';
import {RouteParams} from 'angular2/router';

import {User} from './user';
import {UserService} from './user.service';

@Component({
  selector: 'user-view',
  templateUrl: './components/users/view.html'
})
export class UserViewComponent implements OnInit {
  public user: User;

  constructor(
    private _routeParams: RouteParams,
    private _userService: UserService
  ) {}

  ngOnInit() {
    const id: number = parseInt(this._routeParams.get('id'));

    this._userService
      .getUser(id)
      .then(user => {
        console.info(user);
        this.user = user;
      });
  }
}
user.service.ts:

import {Injectable} from 'angular2/core';

// mock-users is a static JS array
import {users} from './mock-users';
import {User} from './user';

@Injectable()
export class UserService {
  getUsers() : Promise<User[]> {
    return Promise.resolve(users);
  }

  getUser(id: number) : Promise<User> {
    return Promise.resolve(users[id]);
  }
}

route参数已正确通过,但在运行测试之前,视图未更改。如何设置在解决ngOnInit中的承诺后发生的测试?

返回
承诺

ngOnInit():承诺

编辑 在测试中,您将返回一个新的
承诺

it('should have name',injectAsync([TestComponentBuilder],(tcb:TestComponentBuilder)=>{
//创建一个新的承诺,允许对测试何时完成进行更大的控制
//
返回新承诺((解决、拒绝)=>{
createAsync(UserViewComponent)
。然后((rootTC)=>{
//手动调用ngOnInit并将测试放在回调中
//
rootTC.debugElement.componentInstance.ngOnInit()。然后(()=>{
间谍(控制台,'信息');
设uvDOMEl=rootTC.nativeElement;
rootTC.detectChanges();
预期(console.info).已与(0)一起调用;
expect(DOM.queryselectoral(uvDOMEl,'h2').length).toBe(0);
//测试完成了
//
解决();
});
});
}));
}

我也遇到了同样的问题,下面是我如何解决它的。我必须使用fakeAsync和tick

fakeAsync(
      inject([TestComponentBuilder], (tcb: TestComponentBuilder) => {
        tcb
        .overrideProviders(UsersComponent, [
          { provide: UserService, useClass: MockUserService }
        ])
        .createAsync(UsersComponent)
        .then(fixture => {
          fixture.autoDetectChanges(true);
          let component = <UsersComponent>fixture.componentInstance;
          component.ngOnInit();
          flushMicrotasks();
          let element = <HTMLElement>fixture.nativeElement;
          let items = element.querySelectorAll('li');
          console.log(items);
        });
      })
    )
fakeAsync(
注入([TestComponentBuilder],(tcb:TestComponentBuilder)=>{
tcb
.overrideProviders(用户组件[
{provide:UserService,useClass:MockUserService}
])
.createAsync(UsersComponent)
。然后(fixture=>{
夹具。自动检测更改(真);
设component=fixture.componentInstance;
组件。ngOnInit();
刷新微任务();
让元素=fixture.nativeElement;
让items=element.querySelectorAll('li');
控制台日志(项目);
});
})
)

IMO这个用例的最佳解决方案就是创建一个同步的模拟服务。你不能为这个特定的案例做准备。我个人认为“hack”to不是很优雅。你不应该直接调用
ngOnInit
,因为它应该由框架调用

无论如何,您应该已经在使用mock了,因为您只是对组件进行单元测试,不想依赖真正的服务正常工作

要创建一个同步的服务,只需从调用的任何方法返回服务本身。然后,您可以将
然后
捕获
订阅
,如果您使用
可观察的
)方法添加到模拟中,这样它就像一个
承诺

class MockService {
  data;
  error;

  getData() {
    return this;
  }

  then(callback) {
    if (!this.error) {
      callback(this.data);
    }
    return this;
  }

  catch(callback) {
    if (this.error) {
      callback(this.error);
    }
  }

  setData(data) {
    this.data = data;
  }

  setError(error) {
    this.error = error;
  }
}
这有几个好处。首先,它在执行过程中为您提供了对服务的大量控制,因此您可以轻松自定义服务的行为。当然,它是同步的

这是另一个例子

组件中常见的一件事是使用
ActivatedRoute
并订阅其参数。这是异步的,在
ngOnInit
中完成。我倾向于使用它为
ActivatedRoute
params
属性创建一个模拟。
params
属性将一个模拟对象,具有一些对外部世界显示为可观察对象的功能

export class MockParams {
  subscription: Subscription;
  error;

  constructor(private _parameters?: {[key: string]: any}) {
    this.subscription = new Subscription();
    spyOn(this.subscription, 'unsubscribe');
  }

  get params(): MockParams {
    return this;
  }

  subscribe(next: Function, error: Function): Subscription {
    if (this._parameters && !this.error) {
      next(this._parameters);
    }
    if (this.error) {
      error(this.error);
    }
    return this.subscription;
  }
}

export class MockActivatedRoute {
  constructor(public params: MockParams) {}
}
你可以看到我们有一个
subscribe
方法,它的行为类似于一个
可观察的#subscribe
。我们做的另一件事是监视
订阅
,这样我们就可以测试它是否被破坏。在大多数情况下,你将在
ngOnDestroy
中取消订阅。要在测试中设置这些模拟,你可以做一些事情像

let mockParams: MockParams;

beforeEach(() => {
  mockParams = new MockParams({ id: 'one' });
  TestBed.configureTestingModule({
    imports: [ CommonModule ],
    declarations: [ TestComponent ],
    providers: [
      { provide: ActivatedRoute, useValue: new MockActivatedRoute(mockParams) }
    ]
  });
});
现在为路由设置了所有参数,我们可以访问模拟参数,以便设置错误,还可以检查subscription spy以确保其已取消订阅

如果您查看下面的测试,您将看到它们都是同步测试。不需要
async
fakeAsync
,并且它可以顺利通过

以下是完整的测试(使用RC6)

从'@angular/core'导入{Component,OnInit,OnDestroy,DebugElement};
从“@angular/common”导入{CommonModule};
从'@angular/router'导入{ActivatedRoute};
从'rxjs/Subscription'导入{Subscription};
从“@angular/core/testing”导入{TestBed,async};
从“@angular/platform browser”导入{By}”;
@组成部分({
模板:`
{{id}
{{error}}
`
})
导出类TestComponent实现OnInit、OnDestroy{
id:字符串;
错误:字符串;
认购:认购;
构造函数(私有_路由:ActivatedRoute){}
恩戈尼尼特(){
this.subscription=this.\u route.params.subscripte(
(参数)=>{
this.id=params['id'];
},
(错误)=>{
this.error=错误;
}
);
}
恩贡德斯特罗(){
this.subscription.unsubscripte();
}
}
导出类MockParams{
认购:认购;
错误;
构造函数(私有_参数?:{[key:string]:any}){
this.subscription=新订阅();
spyOn(this.subscription,'unsubscripte');
}
获取参数():MockParams{
归还这个;
}
订阅(下一个:函数,错误:函数):订阅{
if(this._参数&&!this.error){
下一步(此参数);
}
if(此错误){
错误(this.error);
}
退回此订阅;
}
}
导出类MockActivatedRoute{
构造函数(公共参数:MockParams){}
}
描述('component:TestComponent',()=>{
让mockParams:mockParams;
在每个之前(()=>{
mockParams=新的mockParams({id:'one'});
TestBed.configureTestingModule({
导入:[CommonModule],
声明:[TestCompone
let mockParams: MockParams;

beforeEach(() => {
  mockParams = new MockParams({ id: 'one' });
  TestBed.configureTestingModule({
    imports: [ CommonModule ],
    declarations: [ TestComponent ],
    providers: [
      { provide: ActivatedRoute, useValue: new MockActivatedRoute(mockParams) }
    ]
  });
});
import { Component, OnInit, OnDestroy, DebugElement } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ActivatedRoute } from '@angular/router';
import { Subscription } from 'rxjs/Subscription';
import { TestBed, async } from '@angular/core/testing';
import { By } from '@angular/platform-browser';

@Component({
  template: `
    <span *ngIf="id">{{ id }}</span>
    <span *ngIf="error">{{ error }}</span>
  `
})
export class TestComponent implements OnInit, OnDestroy {
  id: string;
  error: string;
  subscription: Subscription;

  constructor(private _route: ActivatedRoute) {}

  ngOnInit() {
    this.subscription = this._route.params.subscribe(
      (params) => {
        this.id = params['id'];
      },
      (error) => {
        this.error = error;
      }
    );
  }

  ngOnDestroy() {
    this.subscription.unsubscribe();
  }
}

export class MockParams {
  subscription: Subscription;
  error;

  constructor(private _parameters?: {[key: string]: any}) {
    this.subscription = new Subscription();
    spyOn(this.subscription, 'unsubscribe');
  }

  get params(): MockParams {
    return this;
  }

  subscribe(next: Function, error: Function): Subscription {
    if (this._parameters && !this.error) {
      next(this._parameters);
    }
    if (this.error) {
      error(this.error);
    }
    return this.subscription;
  }
}

export class MockActivatedRoute {
  constructor(public params: MockParams) {}
}

describe('component: TestComponent', () => {
  let mockParams: MockParams;

  beforeEach(() => {
    mockParams = new MockParams({ id: 'one' });
    TestBed.configureTestingModule({
      imports: [ CommonModule ],
      declarations: [ TestComponent ],
      providers: [
        { provide: ActivatedRoute, useValue: new MockActivatedRoute(mockParams) }
      ]
    });
  });

  it('should set the id on success', () => {
    let fixture = TestBed.createComponent(TestComponent);
    fixture.detectChanges();
    let debugEl = fixture.debugElement;
    let spanEls: DebugElement[] = debugEl.queryAll(By.css('span'));
    expect(spanEls.length).toBe(1);
    expect(spanEls[0].nativeElement.innerHTML).toBe('one');
  });

  it('should set the error on failure', () => {
    mockParams.error = 'Something went wrong';
    let fixture = TestBed.createComponent(TestComponent);
    fixture.detectChanges();
    let debugEl = fixture.debugElement;
    let spanEls: DebugElement[] = debugEl.queryAll(By.css('span'));
    expect(spanEls.length).toBe(1);
    expect(spanEls[0].nativeElement.innerHTML).toBe('Something went wrong');
  });

  it('should unsubscribe when component is destroyed', () => {
    let fixture = TestBed.createComponent(TestComponent);
    fixture.detectChanges();
    fixture.destroy();
    expect(mockParams.subscription.unsubscribe).toHaveBeenCalled();
  });
});