4

이 글을 읽은 후, 단지 2 개의 입력 상자와 제출 버튼이있는 간단한 로그인 페이지를 테스트하기로 결정했습니다. 그런 다음이 구성 요소는 LoginService을 사용하여 이러한 데이터를 백엔드에 전달합니다.서비스로 구성 요소를 테스트 할 수 없습니다.

(는 또한 내가 같은 단위 테스트에 새로 온 점에 유의, 그래서 나는이 같은 구성 요소를 테스트하는 방법으로 좋은 방법이 있는지 확실하지 않습니다.) 우선 들어

, 난 단지 원 #username 입력 요소의 초기 값이 비어 있는지 확인하십시오. 하지만 심지어 사양으로 인해 아래에보고 된 문제에 작업 할 수 없습니다 :

Chrome 55.0.2883 (Windows 7 0.0.0) LoginComponent Username field should be empty FAILED 
     Error: DI Error 
     Error: Uncaught (in promise): Error: No provider for Http! 
     TypeError: Cannot read property 'detectChanges' of undefined 
Chrome 55.0.2883 (Windows 7 0.0.0): Executed 4 of 4 (1 FAILED) (0 secs/0.456 secs) 

login.component :

Chrome 55.0.2883 (Windows 7 0.0.0) LoginComponent Username field should be empty FAILED 
     Failed: Unexpected value 'Http' imported by the module 'DynamicTestModule' 
     Error: Unexpected value 'Http' imported by the module 'DynamicTestModule' 
     TypeError: Cannot read property 'detectChanges' of undefined 
Chrome 55.0.2883 (Windows 7 0.0.0): Executed 4 of 4 (1 FAILED) (0 secs/0.348 secs) 

내가 HTTP 모듈을 삭제했을 때,이 오류가 발생했습니다. HTML

<div class="login jumbotron center-block"> 
    <h1>Login</h1> 

    <form (ngSubmit)="onSubmit($event)" #loginForm="ngForm"> 

    <div class="form-group"> 
     <label for="username">Username</label> 
     <input type="text" class="form-control" [(ngModel)]="model.username" name="username" 
       placeholder="Username" #username="ngModel" required> 
     <div [hidden]="username.valid || username.pristine" class="alert alert-danger"> Username is required </div> 
    </div> 
    <div class="form-group"> 
     <label for="password">Password</label> 
     <input type="password" class="form-control" [(ngModel)]="model.password" name="password" placeholder="Password" #password="ngModel" required> 
     <div [hidden]="password.valid || password.pristine" class="alert alert-danger"> Password is required </div> 
    </div> 

    <button type="submit" class="btn btn-default" [disabled]="!loginForm.form.valid" >Submit</button> 
    <a [routerLink]="['/signup']">Click here to Signup</a> 
    </form> 
</div> 

login.component.ts

,536,913,632 10
import { Component }  from '@angular/core'; 
import { Router }   from '@angular/router'; 
import { LoginService } from '../services/login.service'; 
import { User }   from '../extensions/user.class'; 

@Component({ 
    moduleId: module.id, 
    selector: 'login', 
    templateUrl: '../templates/login.component.html', 
    styleUrls: [ '../styles/login.component.css' ], 
    providers: [ LoginService ] 
}) 
export class LoginComponent { 

    private submitted = false; 
    private model = new User(); 

    constructor(
    private router: Router, 
    private loginService: LoginService 
) {} 

    public onSubmit(event: any): void { 
    event.preventDefault(); 
    if (! this.submitted) { 
     this.submitted = true; 

     if (this.model.username && this.model.password) { 
     this.loginService.login(this.model).then((token) => { 
      localStorage.setItem('id_token', token.id); 
      this.router.navigate(['home']); 
     }).catch((error) => this.onLoginFailed(error)); 
     } else { 
     console.warn('No username or password provided'); 
     } 

    } 
    } 

    private onLoginFailed(error: any): void { 
    //// errors are already handled in login-service //// 
    console.error(error); 
    this.submitted = false; /// reset form submit funcitonality /// 
    } 

    public signup(event: any): void { 
    event.preventDefault(); 
    this.router.navigate(['signup']); 
    } 
} 

login.component.spec.ts

import { async }        from '@angular/core/testing'; 

import { FormsModule }      from '@angular/forms'; 
import { RouterTestingModule }    from '@angular/router/testing'; 
import { Component }       from '@angular/core'; 
import { Location }       from '@angular/common'; 

import { LoginComponent }     from './login.component'; 
import { LoginService }      from '../services/login.service'; 
import { Http } from '@angular/http'; 

import { User }   from '../extensions/user.class'; 

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

class LoginServiceStub { 
    login(user: User){ 
    return true; 
    } 
} 

describe('LoginComponent',() => { 
    let comp:  LoginComponent; 
    let fixture: ComponentFixture<LoginComponent>; 
    let de:  DebugElement; 
    let el:  HTMLElement; 
    let location: Location; 

    // async beforeEach 
    beforeEach(async(() => { 

    TestBed.configureTestingModule({ 
     declarations: [ LoginComponent, DummyComponent ], // declare the test component 
     providers: [ 
     { provide: LoginService, useClass: LoginServiceStub } 
     ], 
     imports: [ 
     FormsModule , 
     RouterTestingModule.withRoutes([ 
     { path: 'singup', component: DummyComponent } 
     ]) 
     ] 
    }).compileComponents() // compile template and css 
    .then(() => { 
     fixture = TestBed.createComponent(LoginComponent); 
     comp = fixture.componentInstance; // LoginComponent test instance 
     de = fixture.debugElement.query(By.css('input[name="username"]')); 
     el = de.nativeElement; 
    }); 

    })); 

    it('Username field should be empty',() => { 
    fixture.detectChanges(); 
    expect(el.textContent).toContain(''); 
    }); 

}); 

답변

2

문제는 LoginService구성 요소 수준

@Component({ 
    providers: [ LoginService ] 
}) 

이것은 당신이 시험의 모의를 선언 곳입니다 모듈 수준에서 선언 된 동일한 서비스를 대체 할에서 선언 된 것입니다. 수행 할 수있는 몇 가지 작업이 있습니다.

  1. 서비스를 구성 요소 수준에서 선언하지 마십시오. 구성 요소에 범위를 지정할 좋은 이유가 없으면 @NgModule.providers에 선언하고이를 싱글 톤으로 만듭니다.

  2. 테스트에서 @Component.providers을 무시합니다.

    TestBed.configureTestingModule({}) 
    TestBed.overrideComponent(LoginComponent, { 
        set: { 
        providers: [ 
         { provide: LoginService, useClass: LoginServiceStub } 
        ] 
        } 
    }); 
    
0

Alexus,

당신이 테스트 구성 요소에 HTTP 모듈을 수입하고 "공급자"배열에 추가 시도? 이 경우 모든 의존성을 지정해야한다고 생각합니다. 귀하의 LoginService가 {Http}를 조항으로 요구하고 있다고 가정하고 있지만 테스트 구성 요소는 {Http}를 등록하지 않으므로 사용할 인스턴스를 찾을 수 없습니다.

편집 :

TestBed.configureTestingModule({ 
    declarations: [ LoginComponent, DummyComponent ], // declare the test component 
    providers: [ 
    { provide: LoginService, useClass: LoginServiceStub }, 
    Http, 
    ], 
    imports: [ 
    FormsModule , 
    RouterTestingModule.withRoutes([ 
    { path: 'singup', component: DummyComponent } 
    ]) 
    ] 

DOUBLE 편집! : 당신이 실제로 당신의 장치 동안 요청을 보내려하지 않으므로

는 또한, 당신은 아마는 HTTP 모듈을 조롱 할 것이다 테스트. @ angle/http/testing의 "MockBackend"로 충분합니다.이 경우 로그인 서비스에서 사용하는 "제공"구문을 사용하여 MockBackend를 사용하여 응답을 생성하는 Http 모듈을 제공해야합니다.

관련 문제