title: Angular unit testing form validation
body: <p>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.</p>
  
  <p>Upon testing I'm getting 2 failures </p>
  
  <blockquote>
    <p>RegisterComponent > form should be valid
        Error: Expected validator to return Promise or Observable.</p>
  </blockquote>
  
  <p>and</p>
  
  <blockquote>
    <p>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()</p>
  </blockquote>
  
  <p><strong>register.component.ts</strong></p>
  
  <pre class="lang-js prettyprint-override"><code>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;
    }
  }
  </code></pre>
  
  <p><strong>register.component.spec.ts</strong></p>
  
  <pre class="lang-js prettyprint-override"><code>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', () =&gt; {
    let component: RegisterComponent;
    let fixture: ComponentFixture&lt;RegisterComponent&gt;;
    let de: DebugElement;
    let el: HTMLElement;
    const fakeActivatedRoute = {
      snapshot: {
        queryParams: {
          returnUrl: '/'
        }
      }
    };
    const routerSpy = jasmine.createSpyObj('Router', ['navigate']);
  
    beforeEach(async(() =&gt; {
      TestBed.configureTestingModule({
        imports: [ReactiveFormsModule, FormsModule, HttpClientTestingModule],
        declarations: [RegisterComponent],
        providers: [
          { provide: Router, useValue: routerSpy },
          { provide: ActivatedRoute, useFactory: () =&gt; fakeActivatedRoute }
        ]
  
      }).compileComponents().then(() =&gt; {
        fixture = TestBed.createComponent(RegisterComponent);
        component = fixture.componentInstance;
        component.ngOnInit();
        fixture.detectChanges();
        de = fixture.debugElement.query(By.css('form'));
        el = de.nativeElement;
      });
    }));
  
    beforeEach(() =&gt; {
      fixture = TestBed.createComponent(RegisterComponent);
      component = fixture.componentInstance;
      component.ngOnInit();
      fixture.detectChanges();
    });
  
    it('form invalid when empty', () =&gt; {
      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', () =&gt; {
      const name = component.registerForm.controls.name;
      expect(name.valid).toBeFalsy();
  
      name.setValue('');
      expect(name.hasError('required')).toBeTruthy();
  
    });
  
    it('email field validity', () =&gt; {
      const email = component.registerForm.controls.email;
      expect(email.valid).toBeFalsy();
  
      email.setValue('');
      expect(email.hasError('required')).toBeTruthy();
    });
  
    it('phone field validity', () =&gt; {
      const phone = component.registerForm.controls.phone;
      expect(phone.valid).toBeFalsy();
  
      phone.setValue('');
      expect(phone.hasError('required')).toBeTruthy();
  
    });
  
    it('password field validity', () =&gt; {
      const password = component.registerForm.controls.password;
      expect(password.valid).toBeFalsy();
  
      password.setValue('');
      expect(password.hasError('required')).toBeTruthy();
  
    });
  
    it('confirmPassword field validity', () =&gt; {
      const confirmPassword = component.registerForm.controls.confirmPassword;
      expect(confirmPassword.valid).toBeFalsy();
  
      confirmPassword.setValue('');
      expect(confirmPassword.hasError('required')).toBeTruthy();
  
    });
  
    it('should set submitted to true', () =&gt; {
      component.onSubmit();
      expect(component.submitted).toBeTruthy();
    });
  
    it('should call onSubmit method', () =&gt; {
      spyOn(component, 'onSubmit');
      el = fixture.debugElement.query(By.css('button')).nativeElement;
      el.click();
      expect(component.registerForm).toHaveBeenCalledTimes(1);
    });
  
    it('form should be valid', () =&gt; {
      component.registerForm.controls.name.setValue('sasd');
      component.registerForm.controls.email.setValue('sadasd@asd.com');
      component.registerForm.controls.phone.setValue('132456789');
      component.registerForm.controls.password.setValue('qwerty');
      component.registerForm.controls.confirmPassword.setValue('qwerty');
      expect(component.registerForm.valid).toBeTruthy();
    });
  });
  </code></pre>
  
  <p>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.</p>