组件中的模拟服务仍然需要HttpClient作为提供者

时间:2019-01-28 13:13:59

标签: angular jasmine karma-runner

我有一个使用模拟服务的组件。我是这样创建的。

注意:对于这个问题,我更改了名称,在项目中它具有更特定的名称。

list.component.spec.ts

describe('ListComponent', () => {
  let component: ListComponent;
  let fixture: ComponentFixture<ListComponent>;
  let ITEMS: ListItem[];
  let mockService;

  beforeEach(async(() => {
    ITEMS = require('../../../../../assets/mockdata/items.json');
    mockService = jasmine.createSpyObj(['getItems']);

    TestBed.configureTestingModule({
      imports: [NoopAnimationsModule, RouterTestingModule, SharedModule],
      declarations: [ListComponent, SomePipe, AnotherPipe],
      providers: [
        {
          provide: Service,
          useValue: mockService,
        },
      ],
    }).compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(TimesheetListComponent);
    component = fixture.componentInstance;
    mockService.getItems.and.returnValue(of(ITEMS));
    fixture.detectChanges();
  });

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

list.component.ts

@Component({
  selector: 'app-list',
  templateUrl: './list.component.html',
  styleUrls: ['./list.component.scss'],
})
export class ListComponent implements OnInit {
  // left out props;

  constructor(private service: Service, private router: Router, private route: ActivatedRoute) {
    // This forces the page to reload to the default queryparams when no queryparams are given.
    this.router.routeReuseStrategy.shouldReuseRoute = function() {
      return false;
    };
  }

这会出现以下错误:

Error: StaticInjectorError(DynamicTestModule)[Service -> HttpClient]:
          StaticInjectorError(Platform: core)[Service -> HttpClient]:
            NullInjectorError: No provider for HttpClient!

我也使用此DetailComponent使用了完全不同的组件(Service),但它没有任何抱怨。

我不应该提供HttpClient,因为list.component.ts不依赖于该组件,服务具有此依赖关系,因此我分别测试了该代码。

我真的很困惑,也许我在看东西?非常感谢您的帮助。

1 个答案:

答案 0 :(得分:1)

在测试实例化子组件的组件时也会出现此问题,该子组件也会使用HttpClient注入服务。

为说明起见,请考虑以下组件:

@Component({
  selector: 'app-parent-component',
  template: `<app-child-component></app-child-component>`
})
export class ParentComponent {
  constructor(private service: FooService) { }
}

@Component({
  selector: 'app-child-component',
})
export class ChildComponent {
  constructor(private service: BarService) { }
}

假设FooServiceBarService都注入了HttpClient

根据文档,为ParentComponent进行的测试可能类似于:

beforeEach(async(() => {
  const mockService = jasmine.createSpyObject(['fooMethod']);
  TestBed.configureTestingModule({
    declarations: [ParentComponent],
    providers: [
      {
        provide: FooService,
        useValue: mockService,
      },
    ],
  }).compileComponents();
}));

但是,当angular实例化ParentComponent时,它也实例化ChildComponent,这需要注入BarService。由于您没有告诉TestBed提供模拟,因此它提供了实际的服务,而服务又取决于HttpClient

有许多解决方案。如果要测试ParentComponentChildComponent的集成,最简单的方法是告诉TestBed模拟孩子所需的BarService。如果要维持严格的单元测试,则可以提供一个不向HttpClient注入TestBed服务的模拟子组件:

@Component {(
  selector: 'app-child-component'
)}
export class MockChildComponent {
  constructor() { }
}

并按如下所示设置测试:

TestBed.configureTestingModule({
  declarations: [ParentComponent, ChildComponent],
  providers: [
    {
      provide: Service,
      useValue: mockService,
    },
  ],
}).compileComponents();

或者您可以通过告诉TestBed使用CUSTOM_ELEMENTS_SCHEMA来覆盖组件实例:

TestBed.configureTestingModule({
  declarations: [ParentComponent],
  providers: [
    {
      provide: Service,
      useValue: mockService,
    },
  ],
  schemas: [CUSTOM_ELEMENTS_SCHEMA]
}).compileComponents();