失败:无法为登录测试读取未定义的属性“setValue”

时间:2021-02-10 11:01:48

标签: angular typescript jasmine karma-jasmine karma-runner

我编写了以下测试:

import { DebugElement } from '@angular/core';
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { AuthenticationService } from '../services/authentication.service';
import { By } from '@angular/platform-browser';
import { LoginComponent } from './login.component';
import { ReactiveFormsModule } from '@angular/forms';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { RouterTestingModule } from '@angular/router/testing';
import { GlobalDataService } from '../services/global-data.service';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { DatePipe } from '@angular/common';
import { MatDialogModule } from '@angular/material/dialog';
import { OverlayModule } from '@angular/cdk/overlay';


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

  beforeEach(waitForAsync(() => {
    TestBed.configureTestingModule({
      declarations: [ LoginComponent ],
      imports: [ ReactiveFormsModule, MatFormFieldModule, MatProgressSpinnerModule, RouterTestingModule, HttpClientTestingModule,
         OverlayModule, MatDialogModule ],
      providers: [ GlobalDataService, DatePipe, AuthenticationService ]
    })
    .compileComponents();
  }));

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

  it('should create', () => {
    expect(component).toBeTruthy();
  });

  it('should set instance correctly', () => {
    expect(component).not.toBeNull();
   });

  it('should call auth login method', waitForAsync(() => {
    let loginElement: DebugElement;
    const debugElement = fixture.debugElement;
    const authenticationService = debugElement.injector.get(AuthenticationService);
    const loginSpy = spyOn(authenticationService, 'login').and.callThrough();
    loginElement = fixture.debugElement.query(By.css('form'));
    // to set values
    component.loginForm.controls.username.setValue('test@test.com');
    component.loginForm.controls.password.setValue('testpassword');
    loginElement.triggerEventHandler('ngSubmit', null);
    expect(loginSpy).toHaveBeenCalledTimes(1); // check that service is called once
    // ^ this fails without correct login!
   }));

  it('should render "Login" at the top of the login page', () => {
    fixture.detectChanges();
    const compiled = fixture.debugElement.nativeElement;
    expect(compiled.querySelector('h5').textContent).toContain('Login');
  });
});

我的 Login.component.ts:

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

import { AuthenticationService } from '../services/authentication.service';
import { GlobalDataService } from '../services/global-data.service';

/* tslint:disable variable-name */
@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {

  returnUrl: string;
  loginForm: FormGroup;
  submitted = false;
  error = '';
  loading = false;
  public errorMsg = 'Please login to continue';
  public redirected: boolean;
  public utm_source: string;

  constructor(private router: Router, private formBuilder: FormBuilder, public DataService: GlobalDataService,
              public authenticationService: AuthenticationService, private activatedRoute: ActivatedRoute) {
      this.activatedRoute.queryParams.subscribe(params => {
      const param = params.utm_source;

      if ([ 'a', 'b', 'c', 'd', 'e'].includes(param)) {
        this.redirected = true;
        this.utm_source = param;
      } else {
        this.redirected = false;
      }
  });
  }

  ngOnInit(): void {
    this.loginForm = this.formBuilder.group({
      email: ['', [Validators.required, Validators.email]],
      password: ['', [Validators.required, Validators.minLength(6)]]
  });
  }

// convenience getter for easy access to form fields
get f() { return this.loginForm.controls; }

  onSubmit(loginsubmit) {
    this.submitted = true;
    // stop here if form is invalid
    if (this.loginForm.invalid) {
        return console.log('LoginForm Invalid');
    }
    this.loading = true;

    this.authenticationService.login(this.f.email.value, this.f.password.value)
        .pipe()
        .subscribe(
            data => {
              // it was this.returnUrl instead of avior/dashboard
                if (this.redirected) {
                  this.router.navigate([this.utm_source]);
                } else {
                  this.router.navigate(['landing-page']);
                }
            },
            error => {
                console.log('Login->authservice->err: ', error);
                this.error = error;
                this.loading = false;
            });
}
}

我得到的错误发生在 should call auth login method 测试中,错误如下:

Failed: Cannot read property 'setValue' of undefined

所以看起来 LoginForm 表单不知何故和不知何故未定义。知道为什么会这样吗?表单字段的定义似乎很好,老实说,我不知道出了什么问题。最奇怪的部分是它曾经可以工作,现在迁移代码后就不行了。我尝试修改代码但无济于事。

1 个答案:

答案 0 :(得分:2)

在测试设置中,您试图在表单中不存在的控件上设置一个值。

component.loginForm.controls.username.setValue('test@test.com'); // username doesn't exisit

该属性称为电子邮件。尝试将行更改为:

component.loginForm.controls.email.setValue('test@test.com');