Amrutha
Amrutha

Reputation: 85

Angular Unit testing - Reactive Form value not updating

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);
  }));

Upvotes: 5

Views: 11826

Answers (2)

java-addict301
java-addict301

Reputation: 4136

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.

Upvotes: 0

Evan Kleiner
Evan Kleiner

Reputation: 514

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');
    });

Upvotes: 8

Related Questions