Error: Can't resolve all parameters for (?, ?)
Error: Can't resolve all parameters for (?, ?)
我已经为我的 Angular 10 组件编写了以下单元测试,它基本上显示了具有一些交互性的树视图:
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ReactiveFormsModule } from '@angular/forms';
// import { MatFormFieldModule } from '@angular/material/form-field';
// import { MatInputModule } from '@angular/material/input';
import { MatProgressBarModule } from '@angular/material/progress-bar';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { BrowserModule } from '@angular/platform-browser';
import { TreeviewModule } from 'ngx-treeview';
import { DocumentTreeService } from '../services/DocumentTreeService';
import { DocumentTreeviewComponent } from './document-treeview.component';
describe('DocumentTreeviewComponent', () => {
let component: DocumentTreeviewComponent;
let fixture: ComponentFixture<DocumentTreeviewComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ DocumentTreeviewComponent ],
imports: [ TreeviewModule.forRoot(), ReactiveFormsModule, MatProgressBarModule,
BrowserModule, MatProgressSpinnerModule, /* MatFormFieldModule, MatInputModule */ ],
providers: [ DocumentTreeService ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(DocumentTreeviewComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
我的DocumentTreeService.ts:
import { HttpClient } from '@angular/common/http';
import { TreeviewItem } from 'ngx-treeview';
import { GlobalDataService } from './global-data.service';
interface TreeItem {
name?: string;
text: string;
value: any;
children?: String[];
type: 'folder' | 'document';
}
/* tslint:disable no-string-literal prefer-for-of */
export class DocumentTreeService {
constructor(public http: HttpClient, public DataService: GlobalDataService){}
public treeviewmtextresponse;
public docNames = [];
public FolderItems = [];
public treeviewItem = [];
public finalTreeviewElements: TreeviewItem[] = [];
getDocItems(): TreeviewItem[] {
const docItems = [];
// TEMPORARY SOLUTION (QUICKFIX) FOR 1 DOC
if (!this.treeviewmtextresponse.length) {
const element = this.treeviewmtextresponse['documentName'];
this.docNames.push(element);
const tempArray = element.split('!');
if (!this.FolderItems.includes(tempArray[0])) {
this.FolderItems.push(tempArray[0])
}
docItems[tempArray[0]] = this.docNames.filter(s => s.includes(tempArray[0] + '!'));
for (let o = 0; o < docItems[tempArray[0]].length; o++) {
docItems[tempArray[0]][o] = docItems[tempArray[0]][o].replace(tempArray[0] + '!', '');
const documentItem: TreeItem = {
text: docItems[tempArray[0]][o],
type: 'document',
value: docItems[tempArray[0]][o],
}
docItems[tempArray[0]][o] = documentItem;
}
}
// TEMPORARY SOLUTION (QUICKFIX) FOR 1 DOC
for (let m = 0; m < this.treeviewmtextresponse.length; m++) {
const element = this.treeviewmtextresponse[m]['documentName'];
this.docNames.push(element);
const tempArray = element.split('!');
if (!this.FolderItems.includes(tempArray[0])) {
this.FolderItems.push(tempArray[0])
}
docItems[tempArray[0]] = this.docNames.filter(s => s.includes(tempArray[0] + '!'));
for (let o = 0; o < docItems[tempArray[0]].length; o++) {
docItems[tempArray[0]][o] = docItems[tempArray[0]][o].replace(tempArray[0] + '!', '');
const documentItem: TreeItem = {
text: docItems[tempArray[0]][o],
type: 'document',
value: docItems[tempArray[0]][o],
}
docItems[tempArray[0]][o] = documentItem;
}
}
let jsonString;
for (let p = 0; p < this.FolderItems.length; p++) {
const element = this.FolderItems[p];
const treeItem: TreeItem = {
name: element + "!" + docItems[element][0]["text"],
text: element,
value: element,
type: 'folder',
children: docItems[element],
}
const DokData = treeItem["name"];
this.DataService.SelectedDocumentList.push(DokData);
jsonString = JSON.stringify(treeItem);
const finalObject: TreeviewItem = new TreeviewItem(
JSON.parse(jsonString));
this.finalTreeviewElements.push(finalObject);
}
return this.finalTreeviewElements;
}
}
我的完整错误信息:
Error: Can't resolve all parameters for DocumentTreeService: (?, ?).
我最初以为我把服务放在正确的类别中搞砸了,但经过反复试验和一些逻辑我确定它设置正确。是什么导致了这个问题?
更新 1
我当前的代码库如下所示:
beforeEach(async () => {
const httpSpy = jasmine.createSpyObj('HttpClient', ['get']); //funcName for any function name that you need to exist on the spy objects
const dataSpy = jasmine.createSpyObj('GlobalDataService', ['funcName']);
await TestBed.configureTestingModule({
declarations: [ DocumentTreeviewComponent ],
imports: [ TreeviewModule.forRoot(), ReactiveFormsModule, MatProgressBarModule,
BrowserModule, MatProgressSpinnerModule, HttpClientTestingModule, MatDialogModule, MatInputModule
/* MatFormFieldModule, MatInputModule */ ],
providers: [ GlobalDataService, DatePipe,
{ provide: DocumentTreeService, useValue: mockDocumentTreeService },
{ provide: HttpClient, useValue: httpSpy },
{ provide: GlobalDataService, useValue: dataSpy }] // modify this line to provide a mock
})
.compileComponents();
// To access the service from your tests
documentTreeService = TestBed.inject(DocumentTreeService);
});
更新 2
文档-treeview.component.ts:
import { Component, OnInit } from '@angular/core';
import { TreeviewItem, TreeviewConfig } from 'ngx-treeview';
import { GlobalDataService } from '../services/global-data.service';
import { DocumentTreeService } from '../services/DocumentTreeService';
import { environment } from 'src/environments/environment';
@Component({
selector: 'app-document-treeview',
templateUrl: './document-treeview.component.html',
styleUrls: ['./document-treeview.component.scss']
})
export class DocumentTreeviewComponent implements OnInit {
dropdownEnabled = false;
items: TreeviewItem[];
values: number[];
config = TreeviewConfig.create({
hasAllCheckBox: false,
hasFilter: true,
hasCollapseExpand: true,
maxHeight: 435
});
constructor(public service: DocumentTreeService, public DataService: GlobalDataService) {
this.getDocumentList(false);
}
public docAmount;
ngOnInit(): void {
setInterval(() => {
if (!this.items && this.service.treeviewmtextresponse) {
if (this.service.finalTreeviewElements.length !== 0) {
this.items = this.service.finalTreeviewElements;
} else {
this.items = this.service.getDocItems();
}
}
if (this.DataService.newDocumentAdded === true || this.DataService.documentDeleted === true) {
this.resetDocuments();
if (this.service.treeviewmtextresponse.length > this.docAmount && this.DataService.newDocumentAdded === true || this.service.treeviewmtextresponse.length < this.docAmount && this.DataService.documentDeleted === true) {
this.DataService.newDocumentAdded = false;
this.DataService.documentDeleted = false;
this.docAmount = this.service.treeviewmtextresponse.length;
}
}
}, 1000);
}
public resetDocuments() {
setTimeout(() => {
// reset treeview arrays
this.items = null;
this.service.docNames = [];
this.service.FolderItems = [];
this.service.treeviewItem = [];
this.service.finalTreeviewElements = [];
// reset treeviewmtextresponse
this.service.treeviewmtextresponse = null;
this.getDocumentList(true);
}, 1000);
}
onFilterChange(value: string): void {
console.log('filter:', value);
}
public getFolderName(event) {
// irrelevant for the unit test
}
openTonicUrl(event){
// this.openDialog(event);
if (this.DataService.SelectedDocumentName) {
window.open(`${environment.APIUrl}/text/gui/open/%5Chome%5Ctestproject%5C` + this.DataService.SelectedDocumentName, '_blank');
}
}
public getDocumentList(refresh: boolean) {
return this.service.http.get(this.DataService.getDocumentUrl, this.DataService.httpOptions)
.subscribe(
documentResponse => {
this.service.treeviewmtextresponse = documentResponse['soap:Envelope']['soap:Body'][
'ns2:getDocumentsResponse'
]['return'];
if (refresh === false) {
this.docAmount = this.service.treeviewmtextresponse.length;
}
this.DataService.documentListLoaded = true;
},
error => {
console.log('There was an error: ', error);
});
}
}
您的 DocumentTreeService
使用 HttpClient
和 GlobalDataService
而您没有在 TestBed
模块中提供它们。我会模拟 DocumentTreeService
并且在测试组件时不提供实际服务。
describe('DocumentTreeviewComponent', () => {
let component: DocumentTreeviewComponent;
let fixture: ComponentFixture<DocumentTreeviewComponent>;
let mockDocumentTreeService = jasmine.createSpyObj('documentTreeService', ['getDocItems']); // add this line, also look into how to mock external dependencies such as a service
// !! Remove the <DocumentTreeService> on the above line !!
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ DocumentTreeviewComponent ],
imports: [ TreeviewModule.forRoot(), ReactiveFormsModule, MatProgressBarModule,
BrowserModule, MatProgressSpinnerModule, /* MatFormFieldModule, MatInputModule */ ],
providers: [{ provide: DocumentTreeService, useValue: mockDocumentTreeService }] // modify this line to provide a mock
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(DocumentTreeviewComponent);
component = fixture.componentInstance;
mockDocumentTreeService.getDocItems().and.returnValue([]); // we have mocked getDocItems to return an empty array. You can return anything you want for that function.
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
由于您正在编写单元测试,所以您只想测试组件而不是它的任何依赖项。最佳情况下,您可以 Mock 声明的所有其他组件以及所有提供程序。
一个很好的库是 ng-mocks。然后,您可以模拟它,而不是被迫检查您的服务和您的组件。
providers: [ DocumentTreeService ]
然后可以改成
providers: [ {
provide: DocumentTreeService ,
useValue: MockService(DocumentTreeService)
}]
这样您就可以将外部服务与组件的单元测试分开
服务需要为 class.
使用 @Injectable() 装饰器
例如
@Injectable({providedIn: 'root'}) //if you want to provide it in a certain module scope. Remove the `providedIn`
export class DocumentTreeService {...
如果这不能自行解决。下面是另一种注入具有依赖关系的服务的方法。
let documentTreeService: DocumentTreeService;
beforeEach(async () => {
for any function name that you need to exist on the spy objects
const dataSpy = jasmine.createSpyObj('GlobalDataService', ['funcName']);
await TestBed.configureTestingModule({
//... removed for brevity
imports: [HttpClientTestingModule, ...],
providers: [
DocumentTreeService,
{ provide: GlobalDataService, useValue: dataSpy }
]
})
.compileComponents();
//To access the service from your tests
documentTreeService = TestBed.inject(DocumentTreeService);
});
我已经为我的 Angular 10 组件编写了以下单元测试,它基本上显示了具有一些交互性的树视图:
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ReactiveFormsModule } from '@angular/forms';
// import { MatFormFieldModule } from '@angular/material/form-field';
// import { MatInputModule } from '@angular/material/input';
import { MatProgressBarModule } from '@angular/material/progress-bar';
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
import { BrowserModule } from '@angular/platform-browser';
import { TreeviewModule } from 'ngx-treeview';
import { DocumentTreeService } from '../services/DocumentTreeService';
import { DocumentTreeviewComponent } from './document-treeview.component';
describe('DocumentTreeviewComponent', () => {
let component: DocumentTreeviewComponent;
let fixture: ComponentFixture<DocumentTreeviewComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ DocumentTreeviewComponent ],
imports: [ TreeviewModule.forRoot(), ReactiveFormsModule, MatProgressBarModule,
BrowserModule, MatProgressSpinnerModule, /* MatFormFieldModule, MatInputModule */ ],
providers: [ DocumentTreeService ]
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(DocumentTreeviewComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
我的DocumentTreeService.ts:
import { HttpClient } from '@angular/common/http';
import { TreeviewItem } from 'ngx-treeview';
import { GlobalDataService } from './global-data.service';
interface TreeItem {
name?: string;
text: string;
value: any;
children?: String[];
type: 'folder' | 'document';
}
/* tslint:disable no-string-literal prefer-for-of */
export class DocumentTreeService {
constructor(public http: HttpClient, public DataService: GlobalDataService){}
public treeviewmtextresponse;
public docNames = [];
public FolderItems = [];
public treeviewItem = [];
public finalTreeviewElements: TreeviewItem[] = [];
getDocItems(): TreeviewItem[] {
const docItems = [];
// TEMPORARY SOLUTION (QUICKFIX) FOR 1 DOC
if (!this.treeviewmtextresponse.length) {
const element = this.treeviewmtextresponse['documentName'];
this.docNames.push(element);
const tempArray = element.split('!');
if (!this.FolderItems.includes(tempArray[0])) {
this.FolderItems.push(tempArray[0])
}
docItems[tempArray[0]] = this.docNames.filter(s => s.includes(tempArray[0] + '!'));
for (let o = 0; o < docItems[tempArray[0]].length; o++) {
docItems[tempArray[0]][o] = docItems[tempArray[0]][o].replace(tempArray[0] + '!', '');
const documentItem: TreeItem = {
text: docItems[tempArray[0]][o],
type: 'document',
value: docItems[tempArray[0]][o],
}
docItems[tempArray[0]][o] = documentItem;
}
}
// TEMPORARY SOLUTION (QUICKFIX) FOR 1 DOC
for (let m = 0; m < this.treeviewmtextresponse.length; m++) {
const element = this.treeviewmtextresponse[m]['documentName'];
this.docNames.push(element);
const tempArray = element.split('!');
if (!this.FolderItems.includes(tempArray[0])) {
this.FolderItems.push(tempArray[0])
}
docItems[tempArray[0]] = this.docNames.filter(s => s.includes(tempArray[0] + '!'));
for (let o = 0; o < docItems[tempArray[0]].length; o++) {
docItems[tempArray[0]][o] = docItems[tempArray[0]][o].replace(tempArray[0] + '!', '');
const documentItem: TreeItem = {
text: docItems[tempArray[0]][o],
type: 'document',
value: docItems[tempArray[0]][o],
}
docItems[tempArray[0]][o] = documentItem;
}
}
let jsonString;
for (let p = 0; p < this.FolderItems.length; p++) {
const element = this.FolderItems[p];
const treeItem: TreeItem = {
name: element + "!" + docItems[element][0]["text"],
text: element,
value: element,
type: 'folder',
children: docItems[element],
}
const DokData = treeItem["name"];
this.DataService.SelectedDocumentList.push(DokData);
jsonString = JSON.stringify(treeItem);
const finalObject: TreeviewItem = new TreeviewItem(
JSON.parse(jsonString));
this.finalTreeviewElements.push(finalObject);
}
return this.finalTreeviewElements;
}
}
我的完整错误信息:
Error: Can't resolve all parameters for DocumentTreeService: (?, ?).
我最初以为我把服务放在正确的类别中搞砸了,但经过反复试验和一些逻辑我确定它设置正确。是什么导致了这个问题?
更新 1
我当前的代码库如下所示:
beforeEach(async () => {
const httpSpy = jasmine.createSpyObj('HttpClient', ['get']); //funcName for any function name that you need to exist on the spy objects
const dataSpy = jasmine.createSpyObj('GlobalDataService', ['funcName']);
await TestBed.configureTestingModule({
declarations: [ DocumentTreeviewComponent ],
imports: [ TreeviewModule.forRoot(), ReactiveFormsModule, MatProgressBarModule,
BrowserModule, MatProgressSpinnerModule, HttpClientTestingModule, MatDialogModule, MatInputModule
/* MatFormFieldModule, MatInputModule */ ],
providers: [ GlobalDataService, DatePipe,
{ provide: DocumentTreeService, useValue: mockDocumentTreeService },
{ provide: HttpClient, useValue: httpSpy },
{ provide: GlobalDataService, useValue: dataSpy }] // modify this line to provide a mock
})
.compileComponents();
// To access the service from your tests
documentTreeService = TestBed.inject(DocumentTreeService);
});
更新 2
文档-treeview.component.ts:
import { Component, OnInit } from '@angular/core';
import { TreeviewItem, TreeviewConfig } from 'ngx-treeview';
import { GlobalDataService } from '../services/global-data.service';
import { DocumentTreeService } from '../services/DocumentTreeService';
import { environment } from 'src/environments/environment';
@Component({
selector: 'app-document-treeview',
templateUrl: './document-treeview.component.html',
styleUrls: ['./document-treeview.component.scss']
})
export class DocumentTreeviewComponent implements OnInit {
dropdownEnabled = false;
items: TreeviewItem[];
values: number[];
config = TreeviewConfig.create({
hasAllCheckBox: false,
hasFilter: true,
hasCollapseExpand: true,
maxHeight: 435
});
constructor(public service: DocumentTreeService, public DataService: GlobalDataService) {
this.getDocumentList(false);
}
public docAmount;
ngOnInit(): void {
setInterval(() => {
if (!this.items && this.service.treeviewmtextresponse) {
if (this.service.finalTreeviewElements.length !== 0) {
this.items = this.service.finalTreeviewElements;
} else {
this.items = this.service.getDocItems();
}
}
if (this.DataService.newDocumentAdded === true || this.DataService.documentDeleted === true) {
this.resetDocuments();
if (this.service.treeviewmtextresponse.length > this.docAmount && this.DataService.newDocumentAdded === true || this.service.treeviewmtextresponse.length < this.docAmount && this.DataService.documentDeleted === true) {
this.DataService.newDocumentAdded = false;
this.DataService.documentDeleted = false;
this.docAmount = this.service.treeviewmtextresponse.length;
}
}
}, 1000);
}
public resetDocuments() {
setTimeout(() => {
// reset treeview arrays
this.items = null;
this.service.docNames = [];
this.service.FolderItems = [];
this.service.treeviewItem = [];
this.service.finalTreeviewElements = [];
// reset treeviewmtextresponse
this.service.treeviewmtextresponse = null;
this.getDocumentList(true);
}, 1000);
}
onFilterChange(value: string): void {
console.log('filter:', value);
}
public getFolderName(event) {
// irrelevant for the unit test
}
openTonicUrl(event){
// this.openDialog(event);
if (this.DataService.SelectedDocumentName) {
window.open(`${environment.APIUrl}/text/gui/open/%5Chome%5Ctestproject%5C` + this.DataService.SelectedDocumentName, '_blank');
}
}
public getDocumentList(refresh: boolean) {
return this.service.http.get(this.DataService.getDocumentUrl, this.DataService.httpOptions)
.subscribe(
documentResponse => {
this.service.treeviewmtextresponse = documentResponse['soap:Envelope']['soap:Body'][
'ns2:getDocumentsResponse'
]['return'];
if (refresh === false) {
this.docAmount = this.service.treeviewmtextresponse.length;
}
this.DataService.documentListLoaded = true;
},
error => {
console.log('There was an error: ', error);
});
}
}
您的 DocumentTreeService
使用 HttpClient
和 GlobalDataService
而您没有在 TestBed
模块中提供它们。我会模拟 DocumentTreeService
并且在测试组件时不提供实际服务。
describe('DocumentTreeviewComponent', () => {
let component: DocumentTreeviewComponent;
let fixture: ComponentFixture<DocumentTreeviewComponent>;
let mockDocumentTreeService = jasmine.createSpyObj('documentTreeService', ['getDocItems']); // add this line, also look into how to mock external dependencies such as a service
// !! Remove the <DocumentTreeService> on the above line !!
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ DocumentTreeviewComponent ],
imports: [ TreeviewModule.forRoot(), ReactiveFormsModule, MatProgressBarModule,
BrowserModule, MatProgressSpinnerModule, /* MatFormFieldModule, MatInputModule */ ],
providers: [{ provide: DocumentTreeService, useValue: mockDocumentTreeService }] // modify this line to provide a mock
})
.compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(DocumentTreeviewComponent);
component = fixture.componentInstance;
mockDocumentTreeService.getDocItems().and.returnValue([]); // we have mocked getDocItems to return an empty array. You can return anything you want for that function.
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
由于您正在编写单元测试,所以您只想测试组件而不是它的任何依赖项。最佳情况下,您可以 Mock 声明的所有其他组件以及所有提供程序。
一个很好的库是 ng-mocks。然后,您可以模拟它,而不是被迫检查您的服务和您的组件。
providers: [ DocumentTreeService ]
然后可以改成
providers: [ {
provide: DocumentTreeService ,
useValue: MockService(DocumentTreeService)
}]
这样您就可以将外部服务与组件的单元测试分开
服务需要为 class.
使用 @Injectable() 装饰器例如
@Injectable({providedIn: 'root'}) //if you want to provide it in a certain module scope. Remove the `providedIn`
export class DocumentTreeService {...
如果这不能自行解决。下面是另一种注入具有依赖关系的服务的方法。
let documentTreeService: DocumentTreeService;
beforeEach(async () => {
for any function name that you need to exist on the spy objects
const dataSpy = jasmine.createSpyObj('GlobalDataService', ['funcName']);
await TestBed.configureTestingModule({
//... removed for brevity
imports: [HttpClientTestingModule, ...],
providers: [
DocumentTreeService,
{ provide: GlobalDataService, useValue: dataSpy }
]
})
.compileComponents();
//To access the service from your tests
documentTreeService = TestBed.inject(DocumentTreeService);
});