Angular中的单元测试无法读取属性

问题描述 投票:0回答:2

我有一个下一个组件,它具有在Angular 9.1上编写的注册表单

import { Component, OnInit, EventEmitter, Input, Output } from '@angular/core';
import { FormBuilder, Validators, FormGroup, FormControl, FormGroupDirective, NgForm } from '@angular/forms';

@Component({
  selector: 'app-sign-up-form',
  templateUrl: './sign-up-form.component.html',
  styleUrls: ['./sign-up-form.component.scss']
})
export class SignUpFormComponent implements OnInit {
  @Output() submitedForm = new EventEmitter<any>();
  @Input() accountExist = false;
  public signUpForm: FormGroup;
  public hidePassword = true;

  constructor(private formBuilder: FormBuilder) {}

  ngOnInit() {
    this.signUpForm = this.formBuilder.group({
      email: [
        '',
        [
          Validators.required,
          Validators.pattern('^([A-z0-9_-]+\.)*[A-z0-9_-]+@[a-z0-9_-]+(\.[a-z0-9_-]+)*\.[a-z]{2,6}$')
        ]
      ],
      passwords: this.formBuilder.group({
        password: [
          '',
          [
          Validators.required,
          Validators.pattern('^[a-zA-Z0-9]{8,25}$'),
          ]
        ],
        confirmedPassword: [
          '',
          [
          Validators.required,
          Validators.pattern('^[a-zA-Z0-9]{8,25}$'),
          ]
        ]
        },
         {
          validators: [
            this.validatePasswords,
            Validators.required
          ]
        }
      )
    });
  }

  public onSubmit() {
    if (this.signUpForm.valid) {
      this.submitedForm.emit(this.signUpForm.value);
    }
  }

  public customErrorStateMatcher() {
    return {
      isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
        if (form.submitted && control.parent.invalid) {
          return true;
        } else if (control.invalid && control.touched) {
          return true;
        } else {
          return false;
        }
      }
    };
  }

  private validatePasswords(group: FormControl) {
    const password = group.get('password').value;
    const confirmPass = group.get('confirmedPassword').value;
    return password === confirmPass ? null : { notSame: true };
  }
}

并且有模板:

<div class="sign-up">
  <h2 class="sign-up__header">Sign Up</h2>
  <form class="sign-up__form"
        [formGroup]="signUpForm">
    <div class="sign-up__form-fields-container">
      <mat-form-field class="sign-up__form-field"
                      hideRequiredMarker="true">
        <mat-label>Email</mat-label>
        <input placeholder="[email protected]"
               matInput
               formControlName="email" />
        <mat-error *ngIf="signUpForm.get('email').hasError('required')">
          You must enter a value
        </mat-error>
        <mat-error *ngIf="!signUpForm.get('email').hasError('required')">
          Not a valid email
        </mat-error>
      </mat-form-field>
      <div formGroupName="passwords">
        <mat-form-field class="sign-up__form-field">
          <mat-label style.color="white">Enter your password</mat-label>
          <input matInput
                 formControlName="password"
                 [type]="hidePassword ? 'password' : 'text'" />
          <mat-error *ngIf="signUpForm.get('passwords').get('password').hasError('required')">
            You must enter a value
          </mat-error>
          <mat-error *ngIf="!signUpForm.get('passwords').get('password').hasError('required')">
            Not a valid password
          </mat-error>
          <button type="button"
                  mat-icon-button
                  matSuffix
                  [attr.aria-label]="'Hide password'"
                  [attr.aria-pressed]="hidePassword"
                  (click)="hidePassword = !hidePassword">
            <mat-icon *ngIf="!hidePassword">visibility</mat-icon>
            <mat-icon *ngIf="hidePassword">visibility_off</mat-icon>
          </button>
        </mat-form-field>
        <mat-form-field appearance="legacy"
                        class="sign-up__form-field">
          <mat-label>Confirm password</mat-label>
          <input matInput
                 type="password"
                 formControlName="confirmedPassword"
                 [errorStateMatcher]="customErrorStateMatcher()" />
          <mat-error *ngIf="signUpForm.get('passwords').get('confirmedPassword').hasError('required')">
            You must enter a value
          </mat-error>
          <mat-error *ngIf="this.signUpForm.get('passwords').get('confirmedPassword').value.length">
            Password must match
          </mat-error>
        </mat-form-field>
      </div>
    </div>
    <div class="sign-up__button-wrapper">
      <button class="sign-up__submit"
              (click)="onSubmit()"><span>SUBMIT</span></button>
      <p *ngIf="accountExist"
         class="sign-up__custom-error">Account already exist</p>
    </div>
    <div class="sign-up__account-exist">
      <p class="sign-up__account-question">Already have an account ?</p>
      <a class="sign-up__sign-in-link"
         routerLink="/sign-in">Sign in</a>
      <a mat-button
         href="https://sportbuddies.herokuapp.com/oauth2/authorization/facebook"
         class="sign-up__facebook-btn">
        <span class="sign-up__facebook-icon"></span>
        <span>Sign up with Facebook</span></a>
    </div>
  </form>
  <div>
  </div>
</div>

因此,我正在用Jasmine / Karma编写单元测试,以测试组件的创建,提交表单和验证。

import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { BrowserModule, By } from '@angular/platform-browser';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { DebugElement } from '@angular/core';

import { SignUpFormComponent } from './sign-up-form.component';

describe('SignUpFormComponent', () => {
  let component: SignUpFormComponent;
  let fixture: ComponentFixture<SignUpFormComponent>;
  let debug: DebugElement;
  let el: HTMLElement;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ SignUpFormComponent ],
      imports: [
        BrowserModule,
        FormsModule,
        ReactiveFormsModule
      ]
    })
    .compileComponents().then(() => {
      fixture = TestBed.createComponent(SignUpFormComponent);
      component = fixture.componentInstance;
      debug = fixture.debugElement.query(By.css('form'));
      el = debug.nativeElement;
    });
  }));

  it('should create the form', () => {
    fixture = TestBed.createComponent(SignUpFormComponent);
    const app = fixture.debugElement.componentInstance;
    expect(app).toBeTruthy();
  });

  it('should set submitted to true', async(() => {
    component.onSubmit();
    expect(component.signUpForm).toBeTruthy();
  }));

  it('should call the onSubmit method', async(() => {
    fixture.detectChanges();
    spyOn(component, 'onSubmit');
    el = fixture.debugElement.query(By.css('button')).nativeElement;
    el.click();
    expect(component.onSubmit).toHaveBeenCalledTimes(0);
  }));

  it('form should be invalid', async(() => {
    fixture.detectChanges();
    component.signUpForm.controls['email'].setValue('');
    component.signUpForm.controls['password'].setValue('');
    component.signUpForm.controls['confirmedPassword'].setValue('');
    expect(component.signUpForm.valid).toBeFalsy();
  }));

  it('form should be valid', async(() => {
    component.signUpForm.controls['email'].setValue('[email protected]');
    component.signUpForm.controls['password'].setValue('1234567F');
    component.signUpForm.controls['confirmedPassword'].setValue('1234567F');
    expect(component.signUpForm.valid).toBeTruthy();
  }));
});

但是它显示了我的错误

Failed: Cannot read property 'setValue' of undefined

Failed: Cannot read property 'controls' of undefined

Failed: Cannot read property 'valid' of undefined

所以,为什么这个signUpForm没有被定义,我该怎么办?谢谢。

angular unit-testing jasmine karma-runner
2个回答
0
投票

尝试将FormBuilder添加到providers中的TestBed.configureTestingModule数组中。但是我敢打赌,真正的问题是创建组件时您没有调用fixture.detectChanges,因此ngOnInit无法运行。 fixture.detectChanges之后的第一个createComponentngOnInit运行时。

因此将.then更改为:

.compileComponents().then(() => {
      fixture = TestBed.createComponent(SignUpFormComponent);
      component = fixture.componentInstance;
      debug = fixture.debugElement.query(By.css('form'));
      el = debug.nativeElement;
      fixture.detectChanges(); // add fixture.detectChanges so ngOnInit runs on all later tests
    });

编辑:保留fixture.detectChanges,但您看到passwordsformBuilder.Group,并且将其子项称为passwordconfirmedPasword。在HTML中,您可以使用formGroupName="passwords"对此进行访问。

尝试:

component.signUpForm.controls['passwords'].controls['password'].setValue('1234567F');
component.signUpForm.controls['passwords'].controls['confirmedPassword'].setValue('1234567F');

无论哪种方式,您都以错误的方式访问您想要访问的内容。


0
投票

我用2个答案的一部分重写了这个-最终-起作用了:

 component.signUpForm.controls['email'].setValue('');
 component.signUpForm.controls['passwords'].get('password').setValue('');
 component.signUpForm.controls['passwords'].get('confirmedPassword').setValue('');
© www.soinside.com 2019 - 2024. All rights reserved.