angular - 失败 : Cannot read property 'setValue' of undefined for a login test

标签 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 表单不知何故和不知何故未定义。知道为什么会这样吗?表单字段的定义似乎很好,老实说,我不知道出了什么问题。最奇怪的部分是它曾经可以工作,现在迁移代码后就不行了。我尝试修补代码但无济于事。

最佳答案

在您的测试设置中,您试图在表单中不存在的控件上设置一个值。component.loginForm.controls.username.setValue('test@test.com'); // username doesn't exisit该属性称为电子邮件。尝试将行更改为:component.loginForm.controls.email.setValue('test@test.com');

关于angular - 失败 : Cannot read property 'setValue' of undefined for a login test,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66135629/

相关文章:

javascript - 在 DOM 准备好之前进行 http 调用 Angular

angular - 单击我想在 ionic 项目中显示其详细信息的名称

javascript - 如何以 Angular 6 显示垫表中每一行的垫旋转器

angularjs - 如何在 Jasmine/Protractor 中模拟 angular.module ('myModule',[]).value()

unit-testing - 未调用 stub 函数,而是调用真实版本。为什么?

javascript - 如何用 Jasmine 测试(行为驱动开发)来测试这个?

angular - 为什么 ng-template 在 Angular 中不起作用?

angular - 将组件用作自定义元素和 Angular 组件

javascript - 如果它们的 Prop 不同,如何流动类型组件 Prop 和getDerivedStateFromProps?

typescript - 带有对象字面量和 Typescript 的功能开关盒