Angular Test Select Dropdown

By Arvind Rai, August 07, 2020
This page will walk through Angular unit test select dropdown example. In our component we have two select elements and their property binding are performed by FormControl. Our test cases will be as following.
1. When we change the value of component property, the selected value should be changed of the associated select element.
2. We will test the changing of selected value from dropdown.
3. We will test the calling of component method on change event in select element.

1. Technologies Used

Find the technologies being used in our example.
1. Angular 10.0.0
2. Node.js 12.5.0
3. Jasmine Core 3.5.0
4. Karma 5.0.9

2. Component to Test

Find the component that we will test in our application. It has two select elements. We will test property binding and change event.
person.component.ts
import { Component } from '@angular/core';
import { FormControl } from '@angular/forms';

@Component({
  selector: 'app-person',
  template: `
    <select [formControl]="selectedProfile" (change)="onProfileChange()" class="select-profile">
       <option *ngFor="let prf of profiles" [ngValue]="prf">
          {{ prf.name }}
       </option>
    </select>
    <p>{{selectedProfile.value.name}}</p>
    <select [formControl]="selectedCity" (change)="onCityChange()" class="select-city">
       <option *ngFor="let ct of cities" [ngValue]="ct">
          {{ ct.name }}
       </option>
    </select>
    <div>{{selectedCity.value.name}}</div>    
  `
})
export class PersonComponent {
  profiles = [
    {id: 'dev', name: 'Developer'},
    {id: 'man', name: 'Manager'},
    {id: 'dir', name: 'Director'}
  ];
  cities = [
    {id: 'kcp', name: 'Kanchipuram'},
    {id: 'prg', name: 'Prayagraj'},
    {id: 'twg', name: 'Tawang'}
  ];  
  selectedProfile = new FormControl(this.profiles[0]);
  selectedCity = new FormControl(this.cities[0]);
  onProfileChange() {
    console.log(this.selectedProfile.value.id);
  }  
  onCityChange() {
    console.log(this.selectedCity.value.id);
  }  
} 

3. Configure Test Module

To create the test cases we will configure testing module and initializes our component to test. Find the code snippet that will run before every test case.
let component: PersonComponent;
let fixture: ComponentFixture<PersonComponent>
beforeEach(() => {
  TestBed.configureTestingModule({
    imports: [ReactiveFormsModule],
    declarations: [PersonComponent]
  });
  fixture = TestBed.createComponent(PersonComponent);
  component = fixture.componentInstance;
}); 
ComponentFixture: A fixture for debugging and testing the component. It provides the properties as debugElement and componentInstance etc. It also provides methods such as detectChanges() and whenStable() etc.
TestBed: The TestBed configures and initializes environment for unit testing. The method configureTestingModule() configures testing module where we configure components, services to test.

4. Test Property Binding to Select Element

In this test case we will assign a value to component property and expect that this value should be bound in HTML template.
it('should bind the configured value', async(() => {
  let select: HTMLSelectElement = fixture.debugElement.query(By.css('.select-profile')).nativeElement;
  let p = fixture.debugElement.nativeElement.querySelector('p');
  fixture.detectChanges();
  component.selectedProfile = new FormControl(component.profiles[1]);
  fixture.detectChanges();
  fixture.whenStable().then(() => {
    let text = select.options[select.selectedIndex].label;
    expect(text).toBe('Manager');
    expect(p.textContent).toBe('Manager');
  });
})); 
Find the properties and methods of ComponentFixture used in our test case.
debugElement: This property is the DebugElement associated with the root element of this component.
nativeElement: This property is the native element at the root of the component.
detectChanges(): This method triggers the change detection cycle for the component.
whenStable(): This method gives a Promise that resolves when the fixture is stable.

5. Testing Change Event

In this test case we will select a value from dropdown and trigger change event and then expect that this should be the selected value.
it('should change the value on selection change', async(() => {
  fixture.detectChanges();
  let select: HTMLSelectElement = fixture.debugElement.query(By.css('.select-city')).nativeElement;
  select.value = select.options[2].value;
  select.dispatchEvent(new Event('change'));
  fixture.detectChanges();
  fixture.whenStable().then(() => {
    let text = select.options[select.selectedIndex].label;
    expect(text).toBe('Tawang');
  });
})); 
In this test case we will trigger the change event and test that the component method should be called.
it('should execute the component method on change', fakeAsync(() => {
  fixture.detectChanges();
  let select: HTMLSelectElement = fixture.debugElement.query(By.css('.select-city')).nativeElement;
  spyOn(component, 'onCityChange');
  select.value = select.options[2].value;
  select.dispatchEvent(new Event('change'));
  tick();
  expect(component.onCityChange).toHaveBeenCalled();
})); 
The Jasmine spyOn spies on the given method of the given object and then on calling of that method, fake execution is performed and no actual execution of that method. This is useful to test a method execution when that method has other dependencies and we want to bypass it.

6. Complete Example

person.component.spec.ts
import { ComponentFixture, TestBed, async, fakeAsync, tick } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { ReactiveFormsModule, FormControl } from '@angular/forms';
import { PersonComponent } from './person.component';

describe('PersonComponent', () => {
  let component: PersonComponent;
  let fixture: ComponentFixture<PersonComponent>;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [ReactiveFormsModule],
      declarations: [PersonComponent]
    });
    fixture = TestBed.createComponent(PersonComponent);
    component = fixture.componentInstance;
  });

  it('should bind the configured value', async(() => {
    let select: HTMLSelectElement = fixture.debugElement.query(By.css('.select-profile')).nativeElement;
    let p = fixture.debugElement.nativeElement.querySelector('p');
    fixture.detectChanges();
    component.selectedProfile = new FormControl(component.profiles[1]);
    fixture.detectChanges();
    fixture.whenStable().then(() => {
      let text = select.options[select.selectedIndex].label;
      expect(text).toBe('Manager');
      expect(p.textContent).toBe('Manager');
    });
  }));

  it('should change the value on selection change', async(() => {
    fixture.detectChanges();
    let select: HTMLSelectElement = fixture.debugElement.query(By.css('.select-city')).nativeElement;
    select.value = select.options[2].value;
    select.dispatchEvent(new Event('change'));
    fixture.detectChanges();
    fixture.whenStable().then(() => {
      let text = select.options[select.selectedIndex].label;
      expect(text).toBe('Tawang');
    });
  }));

  it('should execute the component method on change', fakeAsync(() => {
    fixture.detectChanges();
    let select: HTMLSelectElement = fixture.debugElement.query(By.css('.select-city')).nativeElement;
    spyOn(component, 'onCityChange');
    select.value = select.options[2].value;
    select.dispatchEvent(new Event('change'));
    tick();
    expect(component.onCityChange).toHaveBeenCalled();
  }));  
}); 

7. Run Test Cases

To run the test cases, find the steps.
1. Install Angular CLI using link.
2. Download source code using download link given below on this page.
3. Use downloaded src in your Angular CLI application.
4. Run ng test using command prompt. Test result can be seen on command prompt as well as on browser.
5. A chrome for test result will open automatically or we can also use the URL http://localhost:9876/
Find the test result print screen.
Angular Test Select Dropdown

8. References

Basics of testing components
ComponentFixture
Jasmine Spy

9. Download Source Code

POSTED BY
ARVIND RAI
ARVIND RAI
LEARN MORE








©2024 concretepage.com | Privacy Policy | Contact Us