Angular 英雄之旅:测试组件时没有 HttpClient 提供程序

Angular tour-of-heroes: no provider for HttpClient when testing a component

我已经看到很多关于此错误的帖子,但就我而言,hero.service.spec.ts 没有任何错误(至少没有显示任何内容),但我有 No provider for HttpClient当我测试依赖于 Hero 服务的组件时(Hero 服务依赖于 HttpClient,组件本身,不)。

我在所有依赖于 Hero 服务的组件上遇到同样的错误。

这是我的heroes.component.spec.ts

import { ComponentFixture, TestBed } from '@angular/core/testing';

import { HeroesComponent } from './heroes.component';

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

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [ HeroesComponent ]
    })
    .compileComponents();
  });

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

  it('should create', () => {
    expect(component).toBeTruthy();
  });
});

还有我的hero.service.spec.ts

import { TestBed } from '@angular/core/testing';
import { HttpClientTestingModule } from '@angular/common/http/testing';

import { HeroService } from './hero.service';

describe('HeroService', () => {
  let service: HeroService;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [ HttpClientTestingModule ],
      providers: [HeroService]
    })
    service = TestBed.inject(HeroService)
  });

  it('should be created', () => {
    expect(service).toBeTruthy();
  });
});

我是 Angular 测试的新手,当您尝试 运行 官方教程中的 ng test 并且它不起作用时,这很烦人:-)

感谢任何帮助!

我认为您需要在 TestBed 中为 heroes.component.spec.ts 导入 HttpClientTestingModule 并同时提供 HeroService。

您需要像这样在组件测试的 Angular 测试模块中导入 HttpClientTestingModule

import { ComponentFixture, TestBed } from '@angular/core/testing';

import { HeroesComponent } from './heroes.component';

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

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [ HeroesComponent ],
      imports: [ HttpClientTestingModule ],  //  Add this line
    })
    .compileComponents();
  });

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

  it('should create', () => {
    expect(component).toBeTruthy();
  });
});

HeroesComponent依赖于HeroService,而HeroService又依赖于HttpClient。 Angular 必须能够在运行时和测试期间解决所有依赖关系。

在测试期间,我们可以随时通过提供带有测试替身的依赖关系来打破这种依赖关系层次结构。

例如,我们可以用这样的假对象替换 HeroService

const fakeHeroService = {
  getHeroes(): Observable<Hero[]> {
    return of([]);
  }
};

TestBed.configureTestingModule({
  providers: [
    {
      provide: HeroesService,
      useValue: fakeHeroService,
    },
  ],
});

或者我们可以用这样的测试替身替换 HttpClient(我们很少想这样做):

const fakeHttpClient = {
  get(url, options): Observable<Hero[]> {
    return of([]);
  }
};

TestBed.configureTestingModule({
  providers: [
    {
      provide: HttpClient,
      useValue: fakeHttpClient,
    },
  ],
});

HttpClient 有很多依赖项。 HttpClientTestingModule 提供其中一些依赖项的虚假版本和其他依赖项的实际版本。

此 Angular 模块还提供了 HttpTestingController,它允许我们伪造 HTTP 响应并设置对测试期间发出的 HTTP 请求的预期。

这取决于您编写的测试类型以及您想要实现的目标。如果您正在为 HeroesComponent 执行独立的单元测试套件,则应将其所有直接依赖项替换为测试替身。

下面是一个可以替代英雄服务的存根示例。

export const femaleMarvelHeroes: Hero[] = [
  { id: 1, name: 'Black Widow' },
  { id: 2, name: 'Captain Marvel' },
  { id: 3, name: 'Medusa' },
  { id: 4, name: 'Ms. Marvel' },
  { id: 5, name: 'Scarlet Witch' },
  { id: 6, name: 'She-Hulk' },
  { id: 7, name: 'Storm' },
  { id: 8, name: 'Wasp' },
  { id: 9, name: 'Rogue' },
  { id: 10, name: 'Elektra' },
  { id: 11, name: 'Gamora' },
  { id: 12, name: 'Hawkeye (Kate Bishop)' },
];

const heroServiceStub = jasmine.createSpyObj<HeroService>(
  HeroService.name,
  [
    'addHero',
    'deleteHero',
    'getHeroes',
  ]);
heroServiceStub.addHero
  .and.callFake(({ name }: Partial<Hero>) => observableOf({
    id: 42,
    name,
  }, asapScheduler))
  .calls.reset();
heroServiceStub .deleteHero
  .and.callFake((hero: Hero) => of(hero, asapScheduler))
  .calls.reset();
heroServiceStub .getHeroes
  .and.returnValue(of(femaleMarvelHeroes, asapScheduler))
  .calls.reset();

此片段提取自我的 GitHub 存储库 LayZeeDK/ngx-tour-of-heroes-mvp

Testing Angular container components 中了解更多验证协作者集成的技术。