【问题标题】:Test component with a providers使用提供者测试组件
【发布时间】:2023-10-29 22:27:01
【问题描述】:

我有一个服务 SoundPanelService 用于服务隔离场景(如https://angular.io/guide/hierarchical-dependency-injection#scenario-service-isolation

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

  constructor() {
  }
}

我有 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 和 SoundRecorder 通过服务 SoundPanelService 与 soundpanel 通信。

我想测试 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);
    });
  });
   
});

但我得到错误

SoundPanelComponent > 应该在声音面板服务的新唱片上发出声音 预计 spy next 会被调用一次。它被调用了 0 次。

如果我让 SoundPanelService providedIn: 'root' 我设法通过测试,但这不是我想要的,因为我希望 SoundPanelService 被隔离到每个 SoundPanelComponent 和它的孩子(我打算在同一页面上有许多 SoundPanelComponents )。

如何测试这个?

【问题讨论】:

    标签: angular jasmine karma-runner angular8


    【解决方案1】:

    已解决

    用过这个Override component providers

    不得不把代码改成这样:

    1. 引入了.overrideComponent
     beforeEach(async(() => {
        TestBed.configureTestingModule({
          declarations: [
            SoundPanelComponent,
            SoundPlayerComponent,
            SoundRecorderComponent,
            SafeHtmlPipe
          ],
          imports: [HttpClientTestingModule]
        })
        .overrideComponent(SoundPanelComponent, {
          set: {
            providers: [
              { provide: SoundPanelService, useClass: SoundPanelService}
            ]
          }
        })
        .compileComponents();
      }));
    
    1. 从调试元素获取 SoundPanelService:
    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);
        });
      });
    

    测试通过!

    【讨论】: