我正在尝试为调用router.navigate()的组件编写一些测试,但是在声明路由时遇到了错误。我已经阅读了很多东西,并尝试了所有方法,但是它们都会导致某些错误或其他错误。我正在使用Angular 4.0.0。

零件:

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.scss']
})
export class LoginComponent implements OnInit {
  constructor(
    private activatedRoute: ActivatedRoute,
    private authService: AuthService,
    private formBuilder: FormBuilder,
    private jwtService: JwtService,
    private router: Router,
    private storageService: StorageService
  ) { ... }

  ngOnInit() {
  }

  private login(formData: any): void {
    const credentials: any = {
      email: formData.controls.email.value,
      password: formData.controls.password.value
    };
    this.authService.login(credentials).subscribe(res => {
      this.activatedRoute.params.subscribe(params => {
        if (params.returnUrl) {
          this.router.navigate([params.returnUrl]);
        } else {
          this.router.navigate(['/dashboard']);
        }
      });
    }, error => { ... });
  }
}


测试:

describe('LoginComponent', () => {
  let component: any;
  let fixture: ComponentFixture<LoginComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ LoginComponent ],
      imports: [
        SharedModule,
        RouterTestingModule
      ],
      providers: [{
        provide: AuthService,
        useClass: MockAuthService
      }, {
        provide: JwtService,
        useClass: MockJwtService
      }, {
        provide: StorageService,
        useClass: MockStorageService
      }],
      schemas: [ NO_ERRORS_SCHEMA ]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(LoginComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  describe('login', () => {
    it('should call router.navigate with the dashboard route if the login is successful', () => {
      spyOn(component.router, 'navigate');
      component.authService.login.and.returnValue(Observable.of({ access_token: 'fake_token' }));
      component.login(component.loginForm);
      expect(component.router.navigate).toHaveBeenCalledWith(['/dashboard']);
    });
  });
});


这一切给了我以下错误:


zone.js:569未处理的承诺拒绝:无法匹配任何路由。网址段:“仪表板”


因此,我从那里开始研究使用withRoutes添加路线。我不喜欢我需要包含DashboardComponent,因为似乎应该为此提供一些模拟/空白的组件,尤其是因为我不想实际导航并加载其他路线,但我找不到任何东西像那样:

TestBed.configureTestingModule({
  declarations: [ LoginComponent ],
  imports: [
    SharedModule,
    RouterTestingModule.withRoutes([{
      path: 'dashboard',
      component: DashboardComponent
    }])
  ],
  ...
})
.compileComponents();


但是,这只是给我一个新的错误:


组件DashboardComponent不是任何NgModule的一部分,或者该模块尚未导入到您的模块中。


所以我认为也许我需要声明DashboardComponent,所以我将其添加到了声明数组中:

TestBed.configureTestingModule({
  declarations: [ LoginComponent, DashboardComponent ],
  ..
})
.compileComponents();


但是,这只会导致另一个错误:


未处理的承诺拒绝:找不到要加载“ DashboardComponent”的主要出口


在这一点上,似乎必须采用一种更简单的方法来完成此操作,因为这是一个非常常见的场景,但是我已经尝试了其他人说的所有使用方法,而所有方法都将进一步拖延这个兔子漏洞。

最佳答案

事实证明该解决方案非常简单...

只需添加RouterTestingModule就可以了,只需要监视所有测试中的router.navigate即可防止它们尝试实际导航到另一条路线。

describe('LoginComponent', () => {
  let component: any;
  let fixture: ComponentFixture<LoginComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ LoginComponent ],
      imports: [
        SharedModule,
        RouterTestingModule   // This provides the mock router, location and routerLink
      ],
      providers: [{
        provide: AuthService,
        useClass: MockAuthService
      }, {
        provide: JwtService,
        useClass: MockJwtService
      }, {
        provide: StorageService,
        useClass: MockStorageService
      }],
      schemas: [ NO_ERRORS_SCHEMA ]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(LoginComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
    spyOn(component.router, 'navigate');    // This prevents every test from calling the real router.navigate which means I don't need to add routes to RouterTestingModule
  });

  describe('login', () => {
    it('should call router.navigate with the dashboard route if the login is successful', () => {
      spyOn(component.router, 'navigate');
      component.authService.login.and.returnValue(Observable.of({ access_token: 'fake_token' }));
      component.login(component.loginForm);
      expect(component.router.navigate).toHaveBeenCalledWith(['/dashboard']);
    });
  });
});

10-06 11:46