首页 文章

如何为angular2编写登录组件的测试(单元测试)

提问于
浏览
3
This is login.component.ts


 
 
     import { Component, OnInit } from '@angular/core';
    import { Router, ActivatedRoute } from '@angular/router';
    import { FormBuilder, FormGroup, Validators } from '@angular/forms';
    import { CommonModule } from  '@angular/common';


    import { LoginUser } from './loginUser.model'
    import { UserService } from './user.service';
    import { LoaderService } from '../shared/loader.service';

    import 'rxjs/add/operator/toPromise';

    @Component({
        templateUrl: './login.component.html'
    })
    export class LoginComponent implements OnInit {
        errorMessage: string;
        loginForm: FormGroup;
        loginObj = new LoginUser();

        constructor(private userService: UserService, private loaderService: LoaderService, private router: Router, fb: FormBuilder) {
            this.loginForm = fb.group({
                userName: [null, Validators.required],
                password: [null, Validators.required]
            })
            console.log(this.loginForm);
        }

    test() : void{
        console.log("This is a test");
    }
        login(loginObjValue: any): void {
            if (loginObjValue.userName == null || loginObjValue.password == null) {
                console.log('error');
            } else {
                this.loginObj.userName = loginObjValue.userName;
                this.loginObj.password = loginObjValue.password;
                this.loaderService.displayLoader(true);

                this.userService.login(this.loginObj)
                    .then((res) => {
                        console.log("data", res);
                        console.log("$localStorage.currentUser", localStorage);
                        let link = ['customercare/customer-ticket'];
                        this.loaderService.displayLoader(false);

                        this.router.navigate(link);
                    })
                    .catch(error => this.handleError(error));
            }
        }

        private handleError(error: any): Promise<any> {
            this.loaderService.displayLoader(false);

            if (error._body) {
                this.errorMessage = JSON.parse(error._body).error_description;
            }

            console.log('An error occurred', error);
            return Promise.reject(error.message || error);
        }

        ngOnInit(): void {


        }
    } 
 
 



    @Component({
        templateUrl: './login.component.html'
    })
    export class LoginComponent implements OnInit {
        errorMessage: string;
        loginForm: FormGroup;
        loginObj = new LoginUser();

        constructor(private userService: UserService, private loaderService: LoaderService, private router: Router, fb: FormBuilder) {
            this.loginForm = fb.group({
                userName: [null, Validators.required],
                password: [null, Validators.required]
            })
            console.log(this.loginForm);
        }


        login(loginObjValue: any): void {
            if (loginObjValue.userName == null || loginObjValue.password == null) {
                console.log('error');
            } else {
                this.loginObj.userName = loginObjValue.userName;
                this.loginObj.password = loginObjValue.password;
                this.loaderService.displayLoader(true);

                this.userService.login(this.loginObj)
                    .then((res) => {
                        console.log("data", res);
                        console.log("$localStorage.currentUser", localStorage);
                        let link = ['customercare/customer-ticket'];
                        this.loaderService.displayLoader(false);

                        this.router.navigate(link);
                    })
                    .catch(error => this.handleError(error));
            }
        }


    }

部分用户服务代码 . 请参考此代码 . userservice.ts的部分代码 . 请参考此代码 . userservice.ts的部分代码 . 请参考此代码 . userservice.ts的部分代码 . 请参考此代码 . userservice.ts的部分代码 . 请参考此代码 .

@Injectable()
export class UserService {
    private URL = "";

    constructor(private http: Http) { }

    login(loginObj: LoginUser) {

        let body = 'userName=' + loginObj.userName + '&password=' + loginObj.password + '&grant_type=password';

        let headers = new Headers();
        headers.append('Content-Type', 'application/x-www-form-urlencoded');

        return this.http.post(this.URL + '/token', body, { headers: headers })
            .toPromise()
            .then((res: Response) => {
                let data = res.json();
                if (data && data.access_token) {
                    localStorage.setItem('currentUser', JSON.stringify(data));
                }
                return data;
            })
    }

}

我写得太快了:我无法调用登录功能 . 请指导我 .

**

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

    beforeEach(async(() => {
      TestBed.configureTestingModule({
        declarations: [ LoginComponent ],
        providers: [
            { provide: UserService, useValue: UserService },
                   ]
      })

      it('should call the login method from the UserService', 
        inject([TestBed, UserService], fakeAsync((tcb: TestBed, mockUserService: UserService) => {
          spyOn(mockUserService, 'login');
    tcb
            .createComponent(LoginComponent)
            .then((fixture: ComponentFixture<LoginComponent>) => {
              tick();
               fixture.detectChanges();
              expect(mockUserService.login).toHaveBeenCalled();
            });
        }))
      );

    });

**

1 回答

  • 0

    所以我不确定错误是什么,但可能我看到的最重要的事情是在创建组件之前不调用compileComponents .

    beforeEach(async(() => {
          TestBed.configureTestingModule({
            declarations: [ LoginComponent ],
            providers: [
                { provide: UserService, useValue: UserService },
                       ]
          }).compileComponents();
    }); <--- also looked to be missing
    

    这只是我的建议,我不是在一台机器附近测试你的方式是否可行,但我也会在每个之前获取一个对夹具的引用,并使用它来在测试中创建你的组件 . 与上述相同但是:

    beforeEach(async(() => {
          TestBed.configureTestingModule({
            declarations: [ LoginComponent ],
            providers: [
                { provide: UserService, useValue: UserService },
                       ]
          }).compileComponents();
    }); 
    
    beforeEach(async(() => {
          // components are now compiled
          fixture = TestBed.createComponent(LoginComponent);
    });
    

    主要是在你已经创建了它的引用之前 . 您尝试在测试中重新创建该引用,但您可以在beforeEach中分配它并使用它 .

    此外,你的测试并没有真正做任何我能看到触发任何事情的东西 . 如果这是您的目标,您可以验证它是否存在 .

    it('should call the login method when the component does something', 
            inject([UserService], ((userService: UserService) => {
              spyOn(userService, 'login');
              let component = fixture.componentInstance;
              component.doSomething();
              expect(userService.login).toHaveBeenCalled();
              });
     }));
    

    由于您只测试函数被调用,因此实际上不需要在异步中包装任何内容 . 你不是在等待DOM或其他任何东西的响应,只是在调用组件自己的login()方法时,确实验证了服务上的login()方法 . 如果有任何疑问,你可以投入一个无害的fixture.detectChanges(),但我相信即使这样做通常也可以确保如果你在模板中改变某些东西,元素会重新传播 .

    老实说,即使是那种纯粹的单元测试 . 您可以为UserService编写其他单元测试 . 如果你想验证LoginComponent的某些方面,我会编写测试断言,一旦LoginComponent登录就应该变异或者是什么 . (可能是DOM中的文本已经改变等) . 或者,如果您只是测试业务逻辑,则可以拦截登录调用以返回true或false并验证doSomething()是否正确响应 .

相关问题