Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/unit-testing/4.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
Unit testing 模拟路由器.events.subscribe()Angular2_Unit Testing_Angular_Mocking_Jasmine - Fatal编程技术网

Unit testing 模拟路由器.events.subscribe()Angular2

Unit testing 模拟路由器.events.subscribe()Angular2,unit-testing,angular,mocking,jasmine,Unit Testing,Angular,Mocking,Jasmine,在我的app.component.ts中,我有以下ngOnInit功能: ngOnInit() { this.sub = this.router.events.subscribe(e => { if (e instanceof NavigationEnd) { if (!e.url.includes('login')) { this.loggedIn = true; } else { this.lo

在我的app.component.ts中,我有以下ngOnInit功能:

ngOnInit() {
    this.sub = this.router.events.subscribe(e => {
      if (e instanceof NavigationEnd) {
        if (!e.url.includes('login')) {
          this.loggedIn = true;
        } else {
          this.loggedIn = false;
        }
      }
    });
  }
目前我正在测试sub是否不为null,但我想测试覆盖率为100%的函数

我想模拟路由器对象,以便模拟URL,然后测试this.loggedIn是否正确设置


我将如何继续模拟此函数?我试过了,但我不知道如何处理涉及的回调和NavigationEnd。

如果有人在寻找答案,我已经找到了答案:

import { NavigationEnd } from '@angular/router';
import { Observable } from 'rxjs/Observable';

class MockRouter {
  public ne = new NavigationEnd(0, 'http://localhost:4200/login', 'http://localhost:4200/login');
  public events = new Observable(observer => {
    observer.next(this.ne);
    observer.complete();
  });
}

class MockRouterNoLogin {
  public ne = new NavigationEnd(0, 'http://localhost:4200/dashboard', 'http://localhost:4200/dashboard');
  public events = new Observable(observer => {
    observer.next(this.ne);
    observer.complete();
  });
}

我根据Angular docs创建了一个路由器存根版本,该版本使用此方法实现NavigationEnd事件以进行测试:

从'@angular/core'导入{Injectable};
从'@angular/router'导入{NavigationEnd};
从“rxjs”导入{Subject};
@可注射()
出口级路由器{
公共网址;
私有主体=新主体();
public events=this.subject.asObservable();
导航(url:string){
this.url=url;
this.triggerNavEvents(url);
}
triggerNavEvents(url){
让ne=new NavigationEnd(0,url,null);
本。主题。下一个(ne);
}

}
接受的答案是正确的,但这稍微简单一点,您可以替换

public ne = new NavigationEnd(0, 'http://localhost:4200/login', 'http://localhost:4200/login');
  public events = new Observable(observer => {
    observer.next(this.ne);
    observer.complete();
  });
作者:

并在下面找到一个完整的测试文件来测试问题中的功能:

import { NO_ERRORS_SCHEMA } from '@angular/core';
import {
  async,
  TestBed,
  ComponentFixture
} from '@angular/core/testing';

/**
 * Load the implementations that should be tested
 */
import { AppComponent } from './app.component';

import { NavigationEnd, Router } from '@angular/router';
import { Observable } from 'rxjs/Observable';


class MockServices {
  // Router
  public events = Observable.of( new NavigationEnd(0, 'http://localhost:4200/login', 'http://localhost:4200/login'));
}

describe(`App`, () => {
  let comp: AppComponent;
  let fixture: ComponentFixture<AppComponent>;
  let router: Router;

  /**
   * async beforeEach
   */
  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ AppComponent ],
      schemas: [NO_ERRORS_SCHEMA],
      providers: [
        { provide: Router, useClass: MockServices },
      ]
    })
    /**
     * Compile template and css
     */
    .compileComponents();
  }));

  /**
   * Synchronous beforeEach
   */
  beforeEach(() => {
    fixture = TestBed.createComponent(AppComponent);
    comp    = fixture.componentInstance;

    router = fixture.debugElement.injector.get( Router);

    /**
     * Trigger initial data binding
     */
    fixture.detectChanges();
  });

  it(`should be readly initialized`, () => {
    expect(fixture).toBeDefined();
    expect(comp).toBeDefined();
  });

  it('ngOnInit() - test that this.loggedIn is initialised correctly', () => {
    expect(comp.loggedIn).toEqual(true);
  });

});
从'@angular/core'导入{NO_ERRORS_SCHEMA};
进口{
异步,
试验台,
部件夹具
}来自“角度/核心/测试”;
/**
*加载应该测试的实现
*/
从“./app.component”导入{AppComponent};
从'@angular/Router'导入{NavigationEnd,Router};
从“rxjs/Observable”导入{Observable};
类模拟服务{
//路由器
公共事件=可观察到的(新导航结束(0,'http://localhost:4200/login', 'http://localhost:4200/login'));
}
描述(`App`,()=>{
让comp:AppComponent;
let夹具:组件夹具;
让路由器:路由器;
/**
*之前异步
*/
beforeach(异步(()=>{
TestBed.configureTestingModule({
声明:[AppComponent],
模式:[无错误模式],
供应商:[
{提供:路由器,useClass:MockServices},
]
})
/**
*编译模板和css
*/
.compileComponents();
}));
/**
*同步前置
*/
在每个之前(()=>{
fixture=TestBed.createComponent(AppComponent);
comp=夹具。组件状态;
路由器=fixture.debugElement.injector.get(路由器);
/**
*触发初始数据绑定
*/
fixture.detectChanges();
});
它(`should ready initialized`,()=>{
期望(fixture.toBeDefined();
期望(comp.toBeDefined();
});
它('ngOnInit()-测试this.loggedIn是否正确初始化',()=>{
expect(comp.loggedIn)、toEqual(true);
});
});

前面的示例
公共事件=可观察的(新导航结束(0,'http://localhost..'));似乎不符合报应,报应抱怨:

失败:未定义的不是对象(正在评估) 'router.routerState.root') 根路径@

尽管(模拟)路由器实例事件,但订阅回调已在原始app.component.ts的
ngOninit()
中成功运行,即Karma正在测试的主要应用程序组件:

this.sub = this.router.events.subscribe(e => { // successful execution across Karma
事实上,路由器被嘲笑的方式看起来有点不完整,从Karma的角度来看是不准确的:因为
Router.routerState
在运行时没有定义

以下是角度路由器在我这边是如何被“截短”的,包括
路由识别
事件在我的情况下被人为烘焙为
可观察的
s:

class MockRouter {
    public events = Observable.of(new RoutesRecognized(2 , '/', '/',
                                  createRouterStateSnapshot()));
}

const createRouterStateSnapshot = function () {
    const routerStateSnapshot = jasmine.createSpyObj('RouterStateSnapshot', 
                                                     ['toString', 'root']);
    routerStateSnapshot.root = jasmine.createSpyObj('root', ['firstChild']);
    routerStateSnapshot.root.firstChild.data = {
        xxx: false
    };
    return <RouterStateSnapshot>routerStateSnapshot;
};
重述/总结我的内容:

角度/路由器:5.2.9, 业力:2.0.2, 茉莉花芯:2.6.4, 因果报应茉莉花:1.1.2


这是一个非常古老的问题,但我只是在寻找比我拥有的更好的东西时遇到它,在我的情况下,我需要测试几个不同的事件。我的基本方法是将Router.events更改为非只读值,如

 (router as any).events = new BehaviorSubject<any>(null);
 fixture.detectChanges();
 router.events.next(new NavigationEnd(0, 'http://localhost:4200/login', 
 'http://localhost:4200/login'));
  expect(comp.loggedIn).toEqual(true);
(路由器如有)。事件=新行为主体(空);
fixture.detectChanges();
router.events.next(新导航结束(0,'http://localhost:4200/login', 
'http://localhost:4200/login'));
expect(comp.loggedIn)、toEqual(true);
希望这能帮助别人。环顾四周后,我找不到一个更简单的解决方案。

演示了如何使用Jasmine spy执行此操作:

const routerSpy = jasmine.createSpyObj('Router', ['navigateByUrl']);
const heroServiceSpy = jasmine.createSpyObj('HeroService', ['getHeroes']);

TestBed.configureTestingModule({
  providers: [
    { provide: HeroService, useValue: heroServiceSpy },
    { provide: Router,      useValue: routerSpy }
  ]
})

it('当英雄点击时应该告诉路由器导航',()=>{
heroClick();//触发单击第一个内部
//参数传递给router.navigateByUrl()间谍
const spy=router.navigateByUrl作为jasmine.spy;
const navArgs=spy.calls.first().args[0];
//希望导航到组件的第一个英雄的id
const id=comp.heros[0].id;
expect(navArgs).toBe('/heroses/'+id,
“应该为第一个英雄导航到英雄细节”);
});
  • 使用
    ReplaySubject
    模拟
    router.events
  • 将其导出到服务,以便您可以更独立于组件对其进行测试,其他组件也可能希望使用此服务;)
  • 使用
    filter
    而不是
    instanceof
  • 然后为组件添加测试,我认为这很简单,对吗:)
源代码
从'@angular/core'导入{Injectable};
从'@angular/Router'导入{NavigationEnd,Router,RouterEvent};
从“rxjs/operators”导入{filter,map};
从“rxjs”导入{Observable};
@注射的({
providedIn:'根'
})
导出类RouteEventService{
构造函数(专用路由器:路由器){
}
subscribeToRouterEventUrl():可观察{
返回此.router.events
.烟斗(
过滤器(事件=>NavigationEnd的事件实例),
映射((事件:RouterEvent)=>event.url)
);
}
}
测试代码
从'@angular/core/testing'导入{TestBed};
从“”导入{RouteEventService}/
 (router as any).events = new BehaviorSubject<any>(null);
 fixture.detectChanges();
 router.events.next(new NavigationEnd(0, 'http://localhost:4200/login', 
 'http://localhost:4200/login'));
  expect(comp.loggedIn).toEqual(true);
const routerSpy = jasmine.createSpyObj('Router', ['navigateByUrl']);
const heroServiceSpy = jasmine.createSpyObj('HeroService', ['getHeroes']);

TestBed.configureTestingModule({
  providers: [
    { provide: HeroService, useValue: heroServiceSpy },
    { provide: Router,      useValue: routerSpy }
  ]
})
it('should tell ROUTER to navigate when hero clicked', () => {

  heroClick(); // trigger click on first inner <div class="hero">

  // args passed to router.navigateByUrl() spy
  const spy = router.navigateByUrl as jasmine.Spy;
  const navArgs = spy.calls.first().args[0];

  // expecting to navigate to id of the component's first hero
  const id = comp.heroes[0].id;
  expect(navArgs).toBe('/heroes/' + id,
    'should nav to HeroDetail for first hero');
});
import {Injectable} from '@angular/core';
import {NavigationEnd, Router, RouterEvent} from '@angular/router';
import {filter, map} from 'rxjs/operators';
import {Observable} from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class RouteEventService {
  constructor(private router: Router) {
  }

  subscribeToRouterEventUrl(): Observable<string> {
    return this.router.events
      .pipe(
        filter(event => event instanceof NavigationEnd),
        map((event: RouterEvent) => event.url)
      );
  }
}

import {TestBed} from '@angular/core/testing';

import {RouteEventService} from './route-event.service';
import {NavigationEnd, NavigationStart, Router, RouterEvent} from '@angular/router';
import {Observable, ReplaySubject} from 'rxjs';

describe('RouteEventService', () => {
  let service: RouteEventService;
  let routerEventRelaySubject: ReplaySubject<RouterEvent>;
  let routerMock;

  beforeEach(() => {
    routerEventRelaySubject = new ReplaySubject<RouterEvent>(1);
    routerMock = {
      events: routerEventRelaySubject.asObservable()
    };

    TestBed.configureTestingModule({
      providers: [
        {provide: Router, useValue: routerMock}
      ]
    });
    service = TestBed.inject(RouteEventService);
  });

  it('should be created', () => {
    expect(service).toBeTruthy();
  });

  describe('subscribeToEventUrl should', () => {
    it('return route equals to mock url on firing NavigationEnd', () => {
      const result: Observable<string> = service.subscribeToRouterEventUrl();
      const url = '/mock';

      result.subscribe((route: string) => {
        expect(route).toEqual(url);
      });

      routerEventRelaySubject.next(new NavigationEnd(1, url, 'redirectUrl'));
    });

    it('return route equals to mock url on firing NavigationStart', () => {
      const result: Observable<string> = service.subscribeToRouterEventUrl();
      const url = '/mock';

      result.subscribe((route: string) => {
        expect(route).toBeNull();
      });

      routerEventRelaySubject.next(new NavigationStart(1, url, 'imperative', null));
    });
  });
});