【问题标题】:Angular Unit test router.eventsAngular 单元测试 router.events
【发布时间】:2017-12-11 13:03:02
【问题描述】:

我有一个组件可以更新特定路由器事件的变量。如何进行单元测试以确保以下代码正常工作?

router.events.subscribe(event => {
  if (event instanceof NavigationStart) {
    this.isLoading = true;
  } else if (event instanceof NavigationEnd || event instanceof NavigationCancel) {
    this.isLoading = false;
  } else if (event instanceof NavigationError) {
    this.isLoading = false;
    // console.error('effect error: ', error);
  }
});

路由器存根

class MockRouter {
    navigate = jasmine.createSpy('navigate');
    start = new NavigationStart(0, '/home');
    end = new NavigationEnd(1, '/home', '/dashboard');
    events = new Observable(observer => {
      observer.next(this.start);
      observer.next(this.end);
      observer.complete();
    });
  }

【问题讨论】:

  • 跟进这个link你应该创建一个路由器存根
  • 我可以让路由器存根工作,但我不确定如何更新 isLoading 变量
  • return true or false 并检查组件变量
  • 你能用一个简单的订阅者模拟 router.events 吗?没有。
  • @bhantol 那么有什么难的方法呢?

标签: angular unit-testing jasmine


【解决方案1】:

我也遇到过类似的情况,我是这样想的:

describe('router.events', () => {
  const mockStart = of(new NavigationStart(0, '/testUrl'));
  const mockEnd = of(new NavigationEnd(0, '/testUrl', '/testUrlRedirect'));
  const mockCancel = of(new NavigationCancel(0, '/testUrl', '/testReason'));
  const mockError = of(new NavigationError(0, '/testUrl', 'test error'));

  let routerEventSpy: jasmine.Spy;
  beforeEach(() => {
    routerEventSpy = spyOn(component.router, 'events');
  });

  it('should set isLoading to true', () => {
    // Arrange
    routerEventSpy.and.returnValue(mockStart);
    component.isLoading = false; // initial value to make sure code is effective

    // Act
    // Call whatever method contains your router.events.subscribe - for example:
    component.ngOnInit(); // <-- Just an example, you should replace this with your corresponding method

    // Assert
    expect(component.isLoading).toBe(true);
  });

  it('should set isLoading to false for NavigationEnd', () => {
    // Arrange
    routerEventSpy.and.returnValue(mockEnd);
    component.isLoading = true; // initial value, see first test

    // Act
    // Call whatever method contains your router.events.subscribe - for example:
    component.ngOnInit(); // <-- Just an example, see first test

    // Assert
    expect(component.isLoading).toBe(false);
  });

  it('should set isLoading to false for NavigationCancel', () => {
    // Arrange
    routerEventSpy.and.returnValue(mockCancel);
    component.isLoading = true; // initial value, see first test

    // Act
    // Call whatever method contains your router.events.subscribe - for example:
    component.ngOnInit(); // <-- Just an example, see first test

    // Assert
    expect(component.isLoading).toBe(false);
  });

  it('should set isLoading to false for NavigationError', () => {
    // Arrange
    routerEventSpy.and.returnValue(mockError);
    component.isLoading = true; // initial value, see first test

    // Act
    // Call whatever method contains your router.events.subscribe - for example:
    component.ngOnInit(); // <-- Just an example, see first test

    // Assert
    expect(component.isLoading).toBe(false);
  });
});

几点说明:

  • component 应替换为您存储 fixture.componentInstance 的任何内容。它是您测试中的 this
  • 这仍然适用于您在上面设置的路由器,但它没有使用您声明的Observable 及其方法 - spy 正在返回 of 可观察值以触发 subscribe .可能有一种方法可以将路由器存根用于您的目的 (e.g. @Yuri's answer),但您的问题只要求测试该逻辑和 isLoading 变量的方法。
  • 如果您的路由器是protectedprivate(一个很好的做法),您仍然可以使用括号符号监视它 - 例如spyOn(component['router'], 'events')

【讨论】:

    【解决方案2】:

    您需要创建一个简单的路由器存根以允许在测试期间发出各种路由器事件。

    路由器存根:

    // mocked source of events
    const routerEventsSubject = new Subject<RouterEvent>();
    
    const routerStub = {
        events: routerEventsSubject.asObservable()
    };
    
    describe('FooComponent', () => {
        ...
    

    然后你只需使用那个存根:

    ...
    let router: Router;
    
    beforeEach(() => {
        TestBed.configureTestingModule({
            providers: [
                {
                    provide: Router,
                    useValue: routerStub
                }
            ]
        });
        router = TestBed.inject(Router);
    ...
    

    测试如下所示:

    it('should be loading on a navigation start', () => {
        // create a navigation event
        routerEventsSubject.next(new NavigationStart(1, 'start'));
    
        expect(component.isLoading).toBeTruthy();
    });
    

    【讨论】:

    • 只是观察一下,为什么我们使用 let router = Router 和 router = TestBed.inject(Router); ?
    猜你喜欢
    • 2019-03-24
    • 2021-05-27
    • 2020-08-08
    • 2017-04-04
    • 2019-03-24
    • 2016-12-29
    • 2021-02-12
    • 1970-01-01
    相关资源
    最近更新 更多