我用一个在dev
和prod
模式下运行良好的服务工作者做了一个Angular 8.2.14
PWA。
但是当我使用ng test
命令运行测试时,出现以下错误:
NullInjectorError: No provider for SwUpdate!
服务工作进程导入为:
imports: [
ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production }),
],
并用作:
export class AppComponent implements OnInit {
constructor(
private swUpdate: SwUpdate,
) {
}
ngOnInit() {
if (this.swUpdate.isEnabled) {
this.swUpdate.available.subscribe(() => {
const appNewVersion = this.translateService.instant('app.new_version_available');
if (confirm(appNewVersion)) {
window.location.reload();
}
});
}
}
我的测试是:
describe('AppComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
AppComponent
],
imports: [
RouterTestingModule,
MatGridListModule
],
}).compileComponents();
}));
it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
});
});
控制台日志输出:
Chromium 80.0.3987 (Linux 0.0.0) AppComponent should render title FAILED
NullInjectorError: StaticInjectorError(DynamicTestModule)[AppComponent -> SwUpdate]:
StaticInjectorError(Platform: core)[AppComponent -> SwUpdate]:
NullInjectorError: No provider for SwUpdate!
error properties: Object({ ngTempTokenPath: null, ngTokenPath: [ 'AppComponent', Function ], ngDebugContext: DebugContext_({ view: Object({ def: Object({ factory: Function, nodeFlags: 33669121, rootNodeFlags: 33554433, nodeMatchedQueries: 0, flags: 0, nodes: [ Object({ nodeIndex: 0, parent: null, renderParent: null, bindingIndex: 0, outputIndex: 0, checkIndex: 0, flags: 33554433, childFlags: 114688, directChildFlags: 114688, childMatchedQueries: 0, matchedQueries: Object({ }), matchedQueryIds: 0, references: Object({ }), ngContentIndex: null, childCount: 1, bindings: [ ], bindingFlags: 0, outputs: [ ], element: Object({ ns: '', name: 'app-root', attrs: [ ], template: null, componentProvider: Object({ nodeIndex: 1, parent: <circular reference: Object>, renderParent: <circular reference: Object>, bindingIndex: 0, outputIndex: 0, checkIndex: 1, flags: 114688, childFlags: 0, directChildFlags: 0, childMatchedQueries: 0, matchedQueries: Object, matchedQueryIds: 0, references: Object, ngContentIndex: -1, ...
at <Jasmine>
at NullInjector.get (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/fesm2015/core.js:855:1)
at resolveToken (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/fesm2015/core.js:17514:1)
at tryResolveToken (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/fesm2015/core.js:17440:1)
at StaticInjector.get (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/fesm2015/core.js:17266:1)
at resolveToken (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/fesm2015/core.js:17514:1)
at tryResolveToken (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/fesm2015/core.js:17440:1)
at StaticInjector.get (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/fesm2015/core.js:17266:1)
at resolveNgModuleDep (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/fesm2015/core.js:30393:1)
at NgModuleRef_.get (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/fesm2015/core.js:31578:1)
我认为问题出在您的测试中:
describe('AppComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
AppComponent
],
imports: [
RouterTestingModule,
MatGridListModule
],
}).compileComponents();
}));
it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
});
});
在测试中,您将使用组件所需的所有依赖项构建模块。
你的组件依赖于SwUpdate
,你的测试模块既没有imports
也没有providers
。 您需要将一个真实的或(最好(伪造的SwUpdate
实例注入到您的 TestBed 模块中,以便至少对其进行编译。
您可以通过在单独的文件中创建类来模拟SwUpdate
类,我从 https://github.com/maciejtreder/ng-toolkit/blob/master/application/src/app/services/swUpdate-server.mock.service.ts 中获取了这段代码
import { Observable, Subject } from 'rxjs';
import { UpdateActivatedEvent, UpdateAvailableEvent } from '@angular/service-worker/src/low_level';
export class SwUpdateServerMock {
public available: Observable<UpdateAvailableEvent> = new Subject();
public activated: Observable<UpdateActivatedEvent> = new Subject();
public isEnabled: boolean = false;
public checkForUpdate(): Promise<void> {
return new Promise((resolve) => resolve());
}
public activateUpdate(): Promise<void> {
return new Promise((resolve) => resolve());
}
}
然后在测试模块中,告诉TestBed模块使用SwUpdateServerMock
而不是SwUpdate
。
describe('AppComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
AppComponent
],
imports: [
RouterTestingModule,
MatGridListModule
],
providers: [
{ provide: SwUpdate, useClass: SwUpdateServerMock }
]
}).compileComponents();
}));
it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
});
});
所以基本上这将注入一个虚假的服务。
您可以尝试注入真正的SwUpdate
类,然后使用 Jasmine 助手来存根它们:
spyOn(swUpdate, 'isEnabled').and.returnValue(false);
举个例子。这应该有效,但可能需要进一步调整