1

我得到 Failed: Uncaught (in promise): Error: Cannot find primary outlet to load 'UserList'錯誤。Angular 2最終版本路由器導航單元測試錯誤

在組件中使用this._router.navigate(['admin/userlist']);單元測試重定向到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' 
 
    [email protected]:///~/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 
 
    [email protected]:///~/zone.js/dist/zone.js:203:0 <- config/spec-bundle.js:53717:33 
 
    [email protected]:///~/zone.js/dist/async-test.js:42:0 <- config/spec-bundle.js:52757:45 
 
    [email protected]:///~/zone.js/dist/proxy.js:69:0 <- config/spec-bundle.js:53414:47 
 
    [email protected]:///~/zone.js/dist/zone.js:202:0 <- config/spec-bundle.js:53716:42 
 
    [email protected]:///~/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 
 
    [email protected]:///~/zone.js/dist/zone.js:236:0 <- config/spec-bundle.js:53750:42 
 
    [email protected]:///~/zone.js/dist/proxy.js:96:0 <- config/spec-bundle.js:53441:49 
 
    [email protected]:///~/zone.js/dist/zone.js:235:0 <- config/spec-bundle.js:53749:54 
 
    [email protected]:///~/zone.js/dist/zone.js:136:0 <- config/spec-bundle.js:53650:57 
 
    [email protected]:///~/zone.js/dist/zone.js:368:0 <- config/spec-bundle.js:53882:42 
 
    [email protected]:///~/zone.js/dist/zone.js:308:0 <- config/spec-bundle.js:53822:44

+0

'Login'組件模板是否有''?如果不是隻爲測試添加一個虛擬組件。錯誤是說路由組件沒有插座 –

+0

@peeskillet登錄模板中沒有。 – xphong

+0

只需添加一個虛擬組件即可。 –

回答

2

Can you explain how to correctly create the router stub

只是做

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']) 

這更是一個單元測試應該是什麼樣子。你只是想測試組件的行爲。所以你只需檢查它是否在路由器上調用導航方法。