Angular Unit testing - Reactive Form value not updating
Asked Answered
K

2

5

I am new to angular unit testing. Test Scenario: The form view value in the html is equal to the component form value. The email value is retrieved by a shared value and being used in the component registration form. I could retrieve the email value from the component using the reactive forms, but when trying to access via the native element, it gives empty. Below is the component.ts

ngOnit(public serviceEmail) {
    this.assignEmailAvailable();
    this.createRegistration();

    }
assignEmailAvailable() {
 if(service.email){
    this.email = serviceEmail.email;
  }
 }   
createRegistration() {
   this.registerForm = new FormGroup({
       email:new FormControl({value:this.email})
    })
}

In the component.spec.ts, I am gonna call this function and check whether both values are same. component.spec.ts

 beforeEach(() => {
    fixture = TestBed.createComponent(RegisterComponent);
    component = fixture.componentInstance;
    service = TestBed.get(serviceEmail);
  });
it('Registration Form Creation', fakeAsync(() => {
    service.email = "[email protected]";

    fixture.detectChanges();

    component.assignEmailAvailable();
    component.createRegisterForm();

    fixture.detectChanges();
    fixture.whenStable().then(() => {
    const email = fixture.debugElement.query(By.css('input[id="email"]')).nativeElement;
//The value is empty even after creating the form using the component function
    expect(email.value).toBe(component.emailValue);
    });
//THis returns me the value set
    expect(component.registerForm.get('email').value).toBe(component.emailValue);
  }));
Karmen answered 8/1, 2020 at 1:19 Comment(2)
in Form Control initialisation, should it use this.email instead e.g. new FormControl(this.email)?Precious
Hey, Its a typo mistake I corrected.Karmen
E
8

I just spent like 2 hours trying to figure out this problem. From my experience, you have a few problems in your beforeEach() function that you need to resolve. First, you need to import the ReactiveFormsModule into your TestBed so that Change Detection does everything correctly. Second, you need to manually run ngOnInit() and fixture.detectChanges() in your beforeEach() call to set up your form. See my beforeEach() functions below. I've also included the rest of my code that others with this problem may find relevant.

Component Template:


<form [formGroup]='emailForm' (ngSubmit)='handleSubmit()'>
  <div class='form-group'>
    <label for='email'>Email address</label>
    <input type='email' class='form-control' id='email' placeholder='Enter email'
      formControlName='email'>

Component TS:

  constructor(
    private apiService: ApiService,
    private fb: FormBuilder,
    private router: Router
  ) { }

  ngOnInit() {
    this.initializeForm();
  }

  initializeForm(): void {
    this.formSubmitted = false;
    this.serverProcessing = false;
    this.emailForm = this.fb.group({
      email: ['', Validators.compose([Validators.required, Validators.email])]
    });
  }

  get email() {return this.emailForm.get('email');}

Both beforeEach() calls in Spec TS:

beforeEach(
async(() => {

  apiServiceSpy = jasmine.createSpyObj('ApiService', ['sendResetPasswordEmail']);
  routerSpy = jasmine.createSpyObj('Router', ['navigateByUrl']);

  TestBed.configureTestingModule({
    declarations: [ SendPasswordResetEmailComponent ],

    // Need to import this if we're messing with Reactive Form inputs!!
    imports: [ReactiveFormsModule],

    providers: [
      {provide: ApiService, useValue: apiServiceSpy},
      {provide: Router, useValue: routerSpy},
      {provide: FormBuilder},
    ]
  })
  .compileComponents();
})
);

beforeEach(() => {
    fixture = TestBed.createComponent(SendPasswordResetEmailComponent);
    component = fixture.componentInstance;

    // ngOnInit() doesn't get called automatically, so we have to do it ourselves
    component.ngOnInit();

    // Telling the fixture to detect changes is really important to correctly bind data
    // Do this after calling ngOnInit() so changes propagate to the template 
    fixture.detectChanges();
});

Sample Test Specs:

it('should have an error for no username', () => {
    const componentElement: HTMLElement = fixture.nativeElement;
    const emailInput: HTMLInputElement = componentElement.querySelector('input');
    emailInput.value = '@gmail.com';
    emailInput.dispatchEvent(new Event('input'));
    fixture.detectChanges();
    expect(component.email.errors.email).toBeTruthy('@gmail.com valid');
});

it('should have no error if the email is valid', () => {
    const componentElement: HTMLElement = fixture.nativeElement;
    const emailInput: HTMLInputElement = componentElement.querySelector('input');
    emailInput.value = '[email protected]';
    emailInput.dispatchEvent(new Event('input'));
    fixture.detectChanges();
    expect(component.email.errors).toBeFalsy('[email protected] invalid');
    });
Eloisaeloise answered 2/10, 2020 at 21:15 Comment(1)
import FormsModule and ReactiveFormsModule solved my issueGlyceride
H
0

In my case, I have everything setup per Evan's answer but in addition I had to remove the fixture.detectChanges(); call from the beforeEach() method (infact, I don't have it anywhere in the test code now).

Only then did my form changes work inside the test.

Handstand answered 21/9, 2022 at 14:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.