Search code examples
angularjasminekarma-runnerangular8

Test component with providers


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!