[Fixed] Test component with a providers

Issue

I have a service SoundPanelService which is used in service isolation scenario ( like https://angular.io/guide/hierarchical-dependency-injection#scenario-service-isolation )

@Injectable()
export class SoundPanelService {
  recorded = new Subject<Sound>();

  constructor() {
  }
}

and I have SoundPanelComponent

Component({
  selector: 'app-sound-panel',
  templateUrl: './sound-panel.component.html',
  styleUrls: ['./sound-panel.component.css'],
  providers: [SoundPanelService] // Service isolation 
})
export class SoundPanelComponent implements OnInit {
  recorded = new Subject<Sound>();

  constructor(private soundPanelService: SoundPanelService) {
    this.soundPanelService.recorded.subscribe((data) => {
      this.recorded.next(data);
    });
  }

  ngOnInit() {
  }

}

sound-panel.component.html

<app-sound-player></app-sound-player>
<app-sound-recorder></app-sound-recorder>

SoundPlayer and SoundRecorder communicate with soundpanel through service SoundPanelService.

I want to test SoundPanelComponent

import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { DebugElement } from '@angular/core';

import { SoundPanelComponent } from './sound-panel.component';
import { SoundRecorderComponent } from '../sound-recorder/sound-recorder.component';
import { SoundPlayerComponent } from '../sound-player/sound-player.component';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { SoundPanelService } from 'src/app/_services/sound-panel.service';
import { Sound } from 'src/app/_models/Sound';

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

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [
        SoundPanelComponent,
        SoundPlayerComponent,
        SoundRecorderComponent,
        SafeHtmlPipe
      ],
      imports: [HttpClientTestingModule],
      providers: [
      {
        provide: SoundPanelService, useClass: SoundPanelService
      }
      ]
    })
    .compileComponents();
  }));

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

  it('should emit sound on new record from sound panel service',  async () => {
    const s: Sound = {base64: 'data:base64', mimeType: 'audio/wmw'};
    spyOn(component.recorded, 'next').and.callThrough();
    sps = TestBed.get(SoundPanelService);
    sps.recorded.next(s);
    fixture.detectChanges();
    fixture.whenStable().then(res => {
expect(component.recorded.next).toHaveBeenCalledTimes(1);
    });
  });
   
});

but I get error

SoundPanelComponent > should emit sound on new record from sound panel service
Expected spy next to have been called once. It was called 0 times.

If I make SoundPanelService providedIn: ‘root’ I manage to pass tests, but this is not what I want since I want SoundPanelService to be isolated to each SoundPanelComponent and it’s children (I intend to have have many SoundPanelComponents on the same page).

How to test this?

Solution

SOLVED

Used this Override component providers

Had to change code to this:

  1. introduced .overrideComponent
 beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [
        SoundPanelComponent,
        SoundPlayerComponent,
        SoundRecorderComponent,
        SafeHtmlPipe
      ],
      imports: [HttpClientTestingModule]
    })
    .overrideComponent(SoundPanelComponent, {
      set: {
        providers: [
          { provide: SoundPanelService, useClass: SoundPanelService}
        ]
      }
    })
    .compileComponents();
  }));
  1. get SoundPanelService from debug element:
it('should emit sound on new record from sound panel service',  async () => {
    const s: Sound = {base64: 'data:base64', mimeType: 'audio/wmw'};
    spyOn(component.recorded, 'next').and.callThrough();
    sps = fixture.debugElement.injector.get(SoundPanelService) as SoundPanelService;
    sps.recorded.next(s);
    fixture.detectChanges();
    fixture.whenStable().then(res => {
      expect(component.recorded.next).toHaveBeenCalledTimes(1);
    });
  });

Test passed!

Leave a Reply

(*) Required, Your email will not be published