14

I'm learning angular 8 and I'm using Karma for unit testing. I have created a basic registration form which works correctly but I'm facing issues in testing.

Upon testing I'm getting 2 failures

RegisterComponent > form should be valid Error: Expected validator to return Promise or Observable.

and

RegisterComponent > should call onSubmit method Error: : Expected a spy, but got FormGroup({ validator: Function, asyncValidator: null, _onCollectionChange: Function, pristine: true, touched: false, _onDisabledChange: [ ], controls: Object({ name: FormControl({ validator: Function, asyncValidator: null, _onCollectionChange: Function, pristine: true, touched: false, _onDisabledChange: [ Function ], _onChange: [ Function ], _pendingValue: '', value: '', status: 'INVALID', errors: Object({ required: true }), valueChanges: EventEmitter({ _isScalar: false, observers: [ ], closed: false, isStopped: false, hasError: false, thrownError: null, __isAsync: false }), statusChanges: EventEmitter({ _isScalar: false, observers: [ ], closed: false, isStopped: false, hasError: false, thrownError: null, __isAsync: false }), _parent: }), email: FormControl({ validator: Function, asyncValidator: Function, _onCollectionChange: Function, pristine: true, touched: false, _onDisabledChange: [ Function ], _onChange: [ Function ], _pendingValue: '', value: '' .... Usage: expect().toHaveBeenCalledTimes()

register.component.ts

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

import { AuthenticationService } from '@/_services';
import { MustMatch } from '@/_helpers/validators';

@Component({
  selector: 'app-register',
  templateUrl: './register.component.html',
  styleUrls: ['./register.component.scss']
})
@Component({ templateUrl: 'register.component.html' })
export class RegisterComponent implements OnInit {
  registerForm: FormGroup;
  submitted = false;
  returnUrl: string;
  error = '';

  constructor(
    private formBuilder: FormBuilder,
    private route: ActivatedRoute,
    private router: Router,
    private authenticationService: AuthenticationService
  ) {
    if (this.authenticationService.currentUserValue) {
      this.router.navigate(['/']);
    }
  }

  ngOnInit() {
    this.registerForm = this.formBuilder.group({
      name: ['', Validators.required],
      email: ['', Validators.required, Validators.email],
      phone: ['', Validators.required],
      password: ['', Validators.required, Validators.minLength(6)],
      confirmPassword: ['', Validators.required],
    }, {
        validator: MustMatch('password', 'confirmPassword')
      });

    this.returnUrl = this.route.snapshot.queryParams.returnUrl || '/';
  }

  get f() {
    return this.registerForm.controls;
  }

  onSubmit() {
    this.submitted = true;
    if (this.registerForm.invalid) {
      return;
    }

    this.submitted = false;
  }
}

register.component.spec.ts

import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { ReactiveFormsModule, FormsModule } from '@angular/forms';
import { RegisterComponent } from './register.component';
import { ActivatedRoute, Router } from '@angular/router';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { DebugElement } from '@angular/core';

describe('RegisterComponent', () => {
  let component: RegisterComponent;
  let fixture: ComponentFixture<RegisterComponent>;
  let de: DebugElement;
  let el: HTMLElement;
  const fakeActivatedRoute = {
    snapshot: {
      queryParams: {
        returnUrl: '/'
      }
    }
  };
  const routerSpy = jasmine.createSpyObj('Router', ['navigate']);

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [ReactiveFormsModule, FormsModule, HttpClientTestingModule],
      declarations: [RegisterComponent],
      providers: [
        { provide: Router, useValue: routerSpy },
        { provide: ActivatedRoute, useFactory: () => fakeActivatedRoute }
      ]

    }).compileComponents().then(() => {
      fixture = TestBed.createComponent(RegisterComponent);
      component = fixture.componentInstance;
      component.ngOnInit();
      fixture.detectChanges();
      de = fixture.debugElement.query(By.css('form'));
      el = de.nativeElement;
    });
  }));

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

  it('form invalid when empty', () => {
    component.registerForm.controls.name.setValue('');
    component.registerForm.controls.email.setValue('');
    component.registerForm.controls.phone.setValue('');
    component.registerForm.controls.password.setValue('');
    component.registerForm.controls.confirmPassword.setValue('');
    expect(component.registerForm.valid).toBeFalsy();
  });

  it('username field validity', () => {
    const name = component.registerForm.controls.name;
    expect(name.valid).toBeFalsy();

    name.setValue('');
    expect(name.hasError('required')).toBeTruthy();

  });

  it('email field validity', () => {
    const email = component.registerForm.controls.email;
    expect(email.valid).toBeFalsy();

    email.setValue('');
    expect(email.hasError('required')).toBeTruthy();
  });

  it('phone field validity', () => {
    const phone = component.registerForm.controls.phone;
    expect(phone.valid).toBeFalsy();

    phone.setValue('');
    expect(phone.hasError('required')).toBeTruthy();

  });

  it('password field validity', () => {
    const password = component.registerForm.controls.password;
    expect(password.valid).toBeFalsy();

    password.setValue('');
    expect(password.hasError('required')).toBeTruthy();

  });

  it('confirmPassword field validity', () => {
    const confirmPassword = component.registerForm.controls.confirmPassword;
    expect(confirmPassword.valid).toBeFalsy();

    confirmPassword.setValue('');
    expect(confirmPassword.hasError('required')).toBeTruthy();

  });

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

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

  it('form should be valid', () => {
    component.registerForm.controls.name.setValue('sasd');
    component.registerForm.controls.email.setValue('[email protected]');
    component.registerForm.controls.phone.setValue('132456789');
    component.registerForm.controls.password.setValue('qwerty');
    component.registerForm.controls.confirmPassword.setValue('qwerty');
    expect(component.registerForm.valid).toBeTruthy();
  });
});

I can't seem to understand what is causing this issue. Have gone through several docs and tutorials for this but it doesn't seem to work.

2 Answers 2

11

Error: Expected validator to return Promise or Observable.

This means you added your multiple validators wrong. Instead of this:

this.registerForm = this.formBuilder.group({
          name: ['', Validators.required],
          email: ['', Validators.required, Validators.email],
          phone: ['', Validators.required],
          password: ['', Validators.required, Validators.minLength(6)],
          confirmPassword: ['', Validators.required],
        }

Try this:

this.registerForm = this.formBuilder.group({
          name: ['', Validators.required],
          email: ['', [Validators.required, Validators.email]],
          phone: ['', Validators.required],
          password: ['', [Validators.required, Validators.minLength(6)]],
          confirmPassword: ['', Validators.required],
        }

Notice how multiple validators are provided in an array, instead of just comma-seperated.

For your second error, you want to call

expect(component.onSubmit).toHaveBeenCalledTimes(1);

Instead of

expect(component.registerForm).toHaveBeenCalledTimes(1);
Sign up to request clarification or add additional context in comments.

Comments

3
expect(component.onSubmit).toHaveBeenCalledTimes(1)

Replace above line in below code, as you want to check onSubmit method not component.registerForm

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.