如何测试 Angular2 的 router.navigate?

How to test Angular2's router.navigate?

我 运行 在其他单元测试中丢失了 <router-outlet> 消息,但为了有一个很好的孤立示例,我创建了一个 AuthGuard 来检查用户是否已登录以进行某些操作.

这是代码:

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
    if (!this.authService.isLoggedIn()) {
        this.router.navigate(['/login']);
        return false;
    }
    return true;
}

现在我想为此编写一个单元测试。

这就是我开始测试的方式:

beforeEach(() => {
    TestBed.configureTestingModule({
        imports: [
            RouterTestingModule.withRoutes([
                {
                    path: 'login',
                    component: DummyComponent
                }
            ])
        ],
        declarations: [
            DummyComponent
        ],
        providers: [
            AuthGuardService,
            {
                provide: AuthService,
                useClass: MockAuthService
            }
        ]
    });
});

我创建了一个什么都不做的 DummyComponent。 现在我的测试。假装服务 returns false 并触发 this.router.navigate(['/login']):

it('should not let users pass when not logged in', (): void => {
    expect(authGuardService.canActivate(<any>{}, <any>{})).toBe(false);
});

这将引发 "Cannot find primary outlet to load" 的异常。 显然我可以使用 toThrow() 而不是 toBe(false),但这似乎不是一个非常明智的解决方案。由于我在这里测试一项服务,因此没有可以放置 <router-outlet> 标记的模板。我可以模拟路由器并制作我自己的导航功能,但是 RouterTestingModule 有什么意义呢?也许您甚至想检查导航是否有效。

I could mock the router and make my own navigate function, but then what's the point of RouterTestingModule? Perhaps you even want to check that navigation worked.

没有实际意义。如果他只是 auth 守卫的单元测试,那么只需模拟并监视模拟以检查它的 navigate 方法是否使用 login 参数

调用
let router = {
  navigate: jasmine.createSpy('navigate')
}

{ provide: Router, useValue: router }

expect(authGuardService.canActivate(<any>{}, <any>{})).toBe(false);
expect(router.navigate).toHaveBeenCalledWith(['/login']);

单元测试通常应该这样写。要尝试测试任何实际的真实导航,那可能属于端到端测试的范畴。

如果您想在不模拟路由器的情况下测试路由器,您可以将其注入到您的测试中,然后直接监视那里的导航方法。 .and.stub() 会成功,因此调用不会执行任何操作。

describe('something that navigates', () => {
    it('should navigate', inject([Router], (router: Router) => {
      spyOn(router, 'navigate').and.stub();
      expect(authGuardService.canActivate(<any>{}, <any>{})).toBe(false);
      expect(router.navigate).toHaveBeenCalledWith(['/login']);
    }));
  });

这对我有用

describe('navigateExample', () => {
    it('navigate Example', () => {
        const routerstub: Router = TestBed.get(Router);
        spyOn(routerstub, 'navigate');
        component.navigateExample();
    });
});
     it(`editTemplate() should navigate to template build module with query params`, inject(
        [Router],
        (router: Router) => {
          let id = 25;
          spyOn(router, "navigate").and.stub();
          router.navigate(["/template-builder"], {
            queryParams: { templateId: id }
          });
          expect(router.navigate).toHaveBeenCalledWith(["/template-builder"], {
            queryParams: { templateId: id }
          });
        }
      ));

我想到了类似的东西:

describe('TestComponent', () => {
  let component: TestComponent;
  let router: Router;
  let fixture: ComponentFixture<TestComponent>;
  const routerSpy = jasmine.createSpyObj('Router', ['navigate']); // create a router spy


  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [
        HttpClientTestingModule
      ],
      declarations: [TestComponent],
      providers: [
        { provide: Router, useValue: routerSpy } // use routerSpy against Router
      ],
    }).compileComponents();
  }));

  beforeEach(() => {
    router = TestBed.inject(Router); // get instance of router 
    fixture = TestBed.createComponent(TestComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it(`should navigate to 'home' page`, () => {
    component.navigateToHome(); // call router.navigate
    const spy = router.navigate as jasmine.Spy; // create the navigate spy
    const navArgs = spy.calls.first().args[0]; // get the spy values
    expect(navArgs[0]).toBe('/home');
  });
});

受 angular 文档启发:https://angular.io/guide/testing-components-scenarios#routing-component