模拟router.events.subscribe()Angular2

sti*_*rts 19 unit-testing mocking jasmine angular

在我的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;
        }
      }
    });
  }
Run Code Online (Sandbox Code Playgroud)

目前我正在测试sub是否为null但我想测试100%覆盖率的函数.

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

我将如何进行模拟此功能?我尝试了但是我不知道如何通过涉及的回调和NavigationEnd来实现这一点.

sti*_*rts 24

我找到了答案,如果有人正在寻找它:

import {
  addProviders,
  async,
  inject,
  TestComponentBuilder,
  ComponentFixture,
  fakeAsync,
  tick
} from '@angular/core/testing';
import { AppComponent } from './app.component';
import { Router, ROUTER_DIRECTIVES, NavigationEnd } from '@angular/router';
import { HTTP_PROVIDERS } from '@angular/http';
import { LocalStorage, WEB_STORAGE_PROVIDERS } from 'h5webstorage';
import { NavComponent } from '../nav/nav.component';
import { FooterComponent } from '../footer/footer.component';
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();
  });
}
Run Code Online (Sandbox Code Playgroud)

  • 这个答案是不完整的。请提供用法说明。 (6认同)
  • 请提供有关TestingModule配置的信息。 (2认同)
  • 为了让这个工作“提供”模拟类到“TestBed”。`提供者:[{提供:路由器,useClass:MockRouter]` (2认同)

For*_*man 11

我从Angular docs创建了一个版本的路由器存根,它使用这个方法来实现NavigationEnd事件以进行测试:

import {Injectable} from '@angular/core';
import { NavigationEnd } from '@angular/router';
import {Subject} from "rxjs";

@Injectable()
export class RouterStub {
  public url;
  private subject = new Subject();
  public events = this.subject.asObservable();

  navigate(url: string) {
    this.url = url;
    this.triggerNavEvents(url);
  }

  triggerNavEvents(url) {
    let ne = new NavigationEnd(0, url, null);
    this.subject.next(ne);
  }
}
Run Code Online (Sandbox Code Playgroud)


Ton*_*nio 7

接受的答案是正确的,但这有点简单,你可以更换

public ne = new NavigationEnd(0, 'http://localhost:4200/login', 'http://localhost:4200/login');
  public events = new Observable(observer => {
    observer.next(this.ne);
    observer.complete();
  });
Run Code Online (Sandbox Code Playgroud)

通过:

public events = Observable.of( new NavigationEnd(0, 'http://localhost:4200/login', 'http://localhost:4200/login'));
Run Code Online (Sandbox Code Playgroud)

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

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);
  });

});
Run Code Online (Sandbox Code Playgroud)

  • 感谢您提供完整文件。乐于助人 (2认同)

小智 6

这是一个非常古老的问题,但我只是在寻找比我现有的更好的东西时遇到它,就我而言,我需要测试几个不同的事件。我的基本方法是将 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);
Run Code Online (Sandbox Code Playgroud)

希望这对某人有帮助。环顾四周后我找不到更简单的解决方案


Yuq*_* G. 5

  • 用于ReplaySubject<RouterEvent>嘲笑router.events
  • 将其导出到服务,以便您可以独立于组件对其进行测试,并且其他组件可能也想使用此服务;)
  • 使用filter而不是instanceof
  • 然后添加组件的测试,我认为这将是微不足道的:)

源代码

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)
      );
  }
}
Run Code Online (Sandbox Code Playgroud)

测试代码

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 routerEventReplaySubject: ReplaySubject<RouterEvent>;
  let routerMock;

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

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

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

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

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

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

    it('NavigationStart', () => {
      const result: Observable<string> = service.subscribeToRouterEventUrl();
      const url = '/mock';

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

      routerEventReplaySubject.next(new NavigationStart(1, url, 'imperative', null));
    });
  });
});
Run Code Online (Sandbox Code Playgroud)