Angular 2最终版本路由器导航单元测试错误

时间:2016-11-29 14:13:30

标签: unit-testing angular karma-jasmine angular2-routing angular2-testing

我到了 Failed: Uncaught (in promise): Error: Cannot find primary outlet to load 'UserList'错误。

当我使用组件中的admin/userlist对我的登录组件进行单元测试重定向到this._router.navigate(['admin/userlist']);时,会发生这种情况。

的login.html:

<div class="row registration">
  <div id="login-form-control" class="col-xs-12 form-area">
    <h2 class="registration-header">Login</h2>
    <form (ngSubmit)="login()" [formGroup]="form">
      <input id="login-username-textbox" type="text" class="form-control" placeholder="Username" [formControl]="username" required>
      <input id="login-password-textbox" type="password" class="form-control" placeholder="Password" [formControl]="password" required>
      <div *ngIf="errorMessage">
        <span class="help-block error">{{errorMessage}}</span>
      </div>
      <div *ngIf="successMessage">
        <span class="help-block success">{{successMessage}}</span>
      </div>
      <button id="login-submit" type="submit" class="go-btn btn btn-lg btn-success btn-block">Login</button>
    </form>
    <div class="row">
      <a [routerLink]="['../register']">Register</a>
    </div>    
  </div>
</div>

login.ts:

import { Component } from '@angular/core';
import { FormGroup, FormControl, Validators, FormBuilder } from '@angular/forms';
import { Router } from '@angular/router';

import { RegistrationService } from '../shared/services/registration';

@Component({
  selector: 'rg-login',
  templateUrl: './login.html'
})
export class Login {
  form: FormGroup;
  username = new FormControl('', Validators.required);
  password = new FormControl('', Validators.required);

  errorMessage = '';
  successMessage = '';

  constructor(private _registrationService: RegistrationService, private _formBuilder: FormBuilder, private _router: Router) {
    this._createForm();
  }

  ngOnInit() {
  }

  login() {
    this._registrationService.loginUser(this.form.value)
        .subscribe(data => {
          if (data) {
            this.errorMessage = '';
            this.successMessage = 'Login successful';
            this._router.navigate(['admin/userlist']);
          } else {
            this.errorMessage = 'Error';
            this.successMessage = '';
          }
        }, error => {
          this.errorMessage = error;
          this.successMessage = '';
        });
  }

  _createForm() {
    this.form = this._formBuilder.group({
      username:  this.username,
      password: this.password
    });
  }
}

login.spec.ts:

import { Component } from '@angular/core';
import { async, inject, TestBed } from '@angular/core/testing';
import { BaseRequestOptions, ConnectionBackend, Http } from '@angular/http';
import { MockBackend } from '@angular/http/testing';
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
import { RouterTestingModule } from '@angular/router/testing';
import { Observable } from 'rxjs/Rx';

import { RegistrationService } from '../shared/services/registration';
import { Login } from './login';

@Component({
  template: ''
})
class Register { }

@Component({
  template: ''
})
class UserList { }

class MockRegistrationService {
  loginUser(user) {
    return Observable.of({
      username: 'TestUser1',
      password: 'TestPassword1'
    });
  }
}

describe('Login', () => {
  let mockRegistrationService = new MockRegistrationService();

  beforeEach(() => TestBed.configureTestingModule({
    declarations: [
      Login,
      Register,
      UserList
    ],
    providers: [
      Login,
      { provide: RegistrationService, useValue: mockRegistrationService }
    ],
    imports: [
      ReactiveFormsModule,
      RouterTestingModule.withRoutes([
        { path: 'register', component: Register },
        { path: 'admin/userlist', component: UserList }
      ])
    ]
  }));

  it('should successfully login', async(() => {
    let fixture = TestBed.createComponent(Login);
    let loginComponent = fixture.componentInstance;

    fixture.detectChanges();

    loginComponent.login({
      username: 'TestUser1',
      password: 'TestPassword1'
    });

    expect(loginComponent.successMessage).toEqual('Login successful');
    expect(loginComponent.errorMessage).toEqual('');
  }));

});

完整错误:

FAILED TESTS:
  Login
    ✖ should successfully login
      PhantomJS 2.1.1 (Mac OS X 0.0.0)
    Failed: Uncaught (in promise): Error: Cannot find primary outlet to load 'UserList'
    resolvePromise@webpack:///~/zone.js/dist/zone.js:429:0 <- config/spec-bundle.js:53943:75
    webpack:///~/zone.js/dist/zone.js:406:0 <- config/spec-bundle.js:53920:27
    invoke@webpack:///~/zone.js/dist/zone.js:203:0 <- config/spec-bundle.js:53717:33
    onInvoke@webpack:///~/zone.js/dist/async-test.js:42:0 <- config/spec-bundle.js:52757:45
    onInvoke@webpack:///~/zone.js/dist/proxy.js:69:0 <- config/spec-bundle.js:53414:47
    invoke@webpack:///~/zone.js/dist/zone.js:202:0 <- config/spec-bundle.js:53716:42
    run@webpack:///~/zone.js/dist/zone.js:96:0 <- config/spec-bundle.js:53610:49
    webpack:///~/zone.js/dist/zone.js:462:0 <- config/spec-bundle.js:53976:60
    invokeTask@webpack:///~/zone.js/dist/zone.js:236:0 <- config/spec-bundle.js:53750:42
    onInvokeTask@webpack:///~/zone.js/dist/proxy.js:96:0 <- config/spec-bundle.js:53441:49
    invokeTask@webpack:///~/zone.js/dist/zone.js:235:0 <- config/spec-bundle.js:53749:54
    runTask@webpack:///~/zone.js/dist/zone.js:136:0 <- config/spec-bundle.js:53650:57
    drainMicroTaskQueue@webpack:///~/zone.js/dist/zone.js:368:0 <- config/spec-bundle.js:53882:42
    invoke@webpack:///~/zone.js/dist/zone.js:308:0 <- config/spec-bundle.js:53822:44

1 个答案:

答案 0 :(得分:2)

  

您能解释一下如何正确创建路由器存根

只做

let routerStub;

beforeEach(() => {
  routerStub = {
    navigate: jasmine.createSpy("navigate")
  };
  TestBed.configureTestingModule({
    providers: [
      // add router provider
      { provide: Router, useValue: routerStub },
      { provide: RegistrationService, useValue: mockRegistrationService }
    ],
    imports: [
      ReactiveFormsModule,
      // Remove Router Module
    ]
  })
});

然后在测试中,检查是否使用正确的参数调用navigate方法

expect(routerStub.navigate).toHaveBeenCalledWith(['someurl'])

这更像是单元测试应该是什么样子。您只想测试组件的行为。所以你只需检查它是否在路由器上调用了导航方法。