组件中的模拟服务仍然需要 HttpClient 作为提供者
Mocked service in component still demands HttpClient as provider
我有一个使用模拟服务的组件。我是这样创建的。
注意:这个问题我改了名字,在项目中它有一个更具体的名字。
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 不依赖该组件,服务有这个,我单独测试了那个代码。
我真是莫名其妙,我是不是在看什么东西呢?非常感谢任何帮助。
测试实例化 child 组件的组件时可能会出现此问题,该组件还使用 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) { }
}
假设FooService
和BarService
都注入了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
.
有多种解决方案。如果你想测试 ParentComponent
和 ChildComponent
的集成,最简单的做法是告诉 TestBed
模拟 child 所需的 BarService
。如果你想保持严格的单元测试,你可以提供一个模拟的 child 组件,它不会向 TestBed
:
注入 HttpClient
服务
@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();
我有一个使用模拟服务的组件。我是这样创建的。
注意:这个问题我改了名字,在项目中它有一个更具体的名字。
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 不依赖该组件,服务有这个,我单独测试了那个代码。
我真是莫名其妙,我是不是在看什么东西呢?非常感谢任何帮助。
测试实例化 child 组件的组件时可能会出现此问题,该组件还使用 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) { }
}
假设FooService
和BarService
都注入了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
.
有多种解决方案。如果你想测试 ParentComponent
和 ChildComponent
的集成,最简单的做法是告诉 TestBed
模拟 child 所需的 BarService
。如果你想保持严格的单元测试,你可以提供一个模拟的 child 组件,它不会向 TestBed
:
HttpClient
服务
@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();