我正在爲名爲'DashboardComponent'的組件創建一個測試。測試規範很難創建。 Karma拋出錯誤「由模塊'DynamicTestModule'導入的'意外值'DecoratorFactory'」。需要有關Angular2 TestBed配置的幫助。 DecoratorFactory錯誤
我已嘗試刪除代碼,直到錯誤消失,然後添加代碼,直到它重新出現以識別源代碼。但是,這並沒有提供豐富的結果,因爲在使用多種不同配置時代碼會中斷,並且我無法確定哪個配置會導致錯誤存在,並導致錯誤被拋出。我懷疑在所有非失敗嘗試中都存在錯誤的配置,並且某些代碼行(例如添加了it()
調用)會導致錯誤在以前不可見時突然顯示。
我希望有比我更有經驗的人會看到我的錯誤並提供一些建議。其他人在StackOverflow上也遇到了同樣的問題,並且爲他們工作的解決方案似乎不適用於這種情況。
碼圖
爲了使代碼庫更容易消化,我創建了一個UML圖,以顯示我試圖在測試牀配置複製依賴關係。
綠色元件是被測試的元件。包元素被導入到模塊中,並且每個服務都有一個模擬類,我們在它們的位置提供模擬類。我們必須聲明三個組件,因爲DashboardComponent的模板引用了PatientListComponent,而PatientListComponent的模板依賴於PatientListItemComponent。
代碼&文件
我希望不要寫一本小說在這個問題,所以我會添加更多的文件&代碼,否則會在請求。在提出要求之前,我會提供那些與識別問題最相關的文件。
應用程序/儀表板/ dashboard.component.spec.ts
import { DebugElement, NgModule } from '@angular/core';
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
import { FormsModule } from '@angular/forms';
import { By } from '@angular/platform-browser';
import { Router, RouterModule } from '@angular/router';
import { DashboardComponent } from './dashboard.component';
import { PatientListComponent } from '../patient-list/patient-list.component';
import { PatientListItemComponent } from '../patient-list-item/patient-list-item.component';
import { AuthenticationService } from '../authentication.service';
import { PatientSummaryService } from '../patient-summary/patient-summary.service';
import { MockAuthService, MockPatientSummaryService, RouterStub } from './dashboard.mocks';
/*
* DebugElement and By are currently not used. I have left them in the import statements above,
* because any test suite WILL use them when it is fully developed.
*/
describe("DashboardComponent",()=>{
var component : DashboardComponent = null;
var fixture : ComponentFixture<DashboardComponent> = null;
beforeEach(
async(
()=>{
TestBed.configureTestingModule(
{
imports: [ NgModule, RouterModule, FormsModule ],
declarations: [ DashboardComponent, PatientListComponent, PatientListItemComponent ],
providers: [
{ provide: AuthenticationService, useClass: MockAuthService },
{ provide: PatientSummaryService, useClass: MockPatientSummaryService },
{ provide: Router, useClass: RouterStub }
]
}
).compileComponents();
}));
beforeEach(()=>{
fixture = TestBed.createComponent(DashboardComponent);
});
it("has a test",()=>{ expect(1).toBe(1);});
/*
describe("filter section behavior",()=>{});
describe("list display behavior", async(()=>{
describe("filtered-list behavior", async(()=>{
//component.filterList = true;
fixture.detectChanges();
fixture.whenStable().then((done:any)=>{
debugger;
});
}));
describe("unfiltered-list behavior",()=>{
//component.filterList = false;
});
}));
*/
});
應用程序/儀表板/ dashboard.mocks.ts
import { DebugElement, NgModule } from '@angular/core';
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
import { FormsModule } from '@angular/forms';
import { By } from '@angular/platform-browser';
import { Router, RouterModule } from '@angular/router';
import { DashboardComponent } from './dashboard.component';
import { PatientListComponent } from '../patient-list/patient-list.component';
import { PatientListItemComponent } from '../patient-list-item/patient-list-item.component';
import { AuthenticationService } from '../authentication.service';
import { PatientSummaryService } from '../patient-summary/patient-summary.service';
import { MockAuthService, MockPatientSummaryService, RouterStub } from './dashboard.mocks';
/*
* DebugElement and By are currently not used. I have left them in the import statements above,
* because any test suite WILL use them when it is fully developed.
*/
describe("DashboardComponent",()=>{
var component : DashboardComponent = null;
var fixture : ComponentFixture<DashboardComponent> = null;
beforeEach(
async(
()=>{
TestBed.configureTestingModule(
{
imports: [ NgModule, RouterModule, FormsModule ],
declarations: [ DashboardComponent, PatientListComponent, PatientListItemComponent ],
providers: [
{ provide: AuthenticationService, useClass: MockAuthService },
{ provide: PatientSummaryService, useClass: MockPatientSummaryService },
{ provide: Router, useClass: RouterStub }
]
}
).compileComponents();
}));
beforeEach(()=>{
fixture = TestBed.createComponent(DashboardComponent);
});
it("has a test",()=>{ expect(1).toBe(1);});
/*
describe("filter section behavior",()=>{});
describe("list display behavior", async(()=>{
describe("filtered-list behavior", async(()=>{
//component.filterList = true;
fixture.detectChanges();
fixture.whenStable().then((done:any)=>{
debugger;
});
}));
describe("unfiltered-list behavior",()=>{
//component.filterList = false;
});
}));
*/
});
應用程序/儀表板/ dashboard.component
import { Component, Input } from '@angular/core';
import { Router } from '@angular/router';
import { Authentication } from '../authentication';
import { AuthenticationService } from '../authentication.service';
import { PatientSummaryService } from '../patient-summary/patient-summary.service';
import { PatientSummary } from '../patient-summary/patient-summary';
@Component(
{
moduleId: module.id,
selector: 'dashboard',
template: `
<div class="container" *ngIf="credentials.valid">
<div class="col-xs-12 filterOptions">
<span class="col-xs-12">
<button class="btn btn-small btn-default pull-right" (click)="toggleFilterView()">Toggle Filters</button>
<h4>Filter Options</h4>
</span>
<span *ngIf="viewFilters">
<label>
<input type='checkbox' [(ngModel)]="filterList" />
Filter the list for <strong>only</strong> patients linked to your account.
</label>
<div class="form-group">
<label>Filter By Patient Name</label>
<input class="form-control" [(ngModel)]="nameFilter" placeholder="Patient name in full or in part." />
</div>
</span>
</div>
<h1>Priority Patients</h1>
<patient-list [sourceData]="todaysPatientList | staffFilter : acceptableStaff" (clickPatient)="selectPatient($event)"></patient-list>
<h1>Patients Records <small>(Not Yet Complete)</small></h1>
<patient-list [sourceData]="nonActivePatientList | staffFilter : acceptableStaff" (clickPatient)="selectPatient($event)"></patient-list>
</div>`,
styles: [
`.filterOptions {
background-color: hsla(187, 55%, 90%, 0.5);
padding: 1em;
border: solid 3px black;
border-radius: 1em;
margin-bottom: 1em;
}`
]
}
)
export class DashboardComponent {
credentials : Authentication = new Authentication(null,null,null);
viewFilters: boolean = false;
nameFilter: string = "";
filterList: boolean = true;
patientSummary: PatientSummary[];
constructor(private patientSummaryService : PatientSummaryService,
private authService : AuthenticationService,
private router : Router){}
ngOnInit(){
var app = this;
this.patientSummaryService.updatedList.subscribe(
(list : PatientSummary[]) => {app.setPatientSummaryList(list);}
);
this.authService.newCreds.subscribe(
(creds : Authentication) => this.credentials = creds
);
this.authService.invalidate.subscribe(
(obj : any) => this.credentials = new Authentication(null,null,null)
);
}
setPatientSummaryList(list: PatientSummary[]) {
var app = this;
list.sort((a: PatientSummary, b: PatientSummary) => {
var dateA = app.extractDate(a);
var dateB = app.extractDate(b);
if (dateA > dateB) return 1;
if (dateA < dateB) return -1;
return 0;
});
this.patientSummary = list;
}
extractDate(item: PatientSummary) {
var date = item.arrivalTime;
if (date === null || date < item.visit.date) {
date = item.visit.date;
}
return date;
}
nameFilterFunction(item: PatientSummary) {
if (this.nameFilter == "") return true;
if (typeof item == "object" && typeof item.name != "undefined") {
var index = item.name.indexOf(this.nameFilter);
return (index !== -1);
}
return false;
}
toggleFilterView() {
this.viewFilters = !this.viewFilters;
}
/**
* Returns a list of patients in ascending order (oldest first) of items
* that are today and are assigned to a room.
*/
get todaysPatientList() {
var app = this;
if (!Array.isArray(this.patientSummary)) return [];
var list = this.patientSummary.filter(
(item: PatientSummary) => {
var date = app.extractDate(item);
var now = new Date();
var today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
var tomorrow = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1);
return date >= today && date <= tomorrow;
}).filter((item: PatientSummary) => {
if (typeof item == "object" && typeof item.location == "object" && typeof item.location.room !== null) {
return item.location.room != "No Room Assignment";
} else {
return true;
}
});
return list.filter((item) => {return app.nameFilterFunction(item);});
}
/**
* Returns a list of patients in descending order (most recent first) of items
* that do not appear in the todaysPatientList attribute;
*/
get nonActivePatientList() {
if (!Array.isArray(this.patientSummary)) return [];
var app = this;
var list = this.todaysPatientList;
var nonActiveList = this.patientSummary.filter((obj: PatientSummary) => {
var index = list.indexOf(obj);
return (index == -1);
});
nonActiveList.reverse();
return nonActiveList.filter((item) => {return app.nameFilterFunction(item);});;
}
get acceptableStaff() {
if (!this.filterList) {
return "any";
} else {
var user = "any";
if (this.credentials instanceof Authentication) {
user = this.credentials.username;
}
if (user === null) user = "any";
return user;
}
};
selectPatient(patient : PatientSummary){
var id = patient.medfaceId;
this.router.navigate(['/detail',id]);
}
}
更新:2017年2月9日下午3點10分PCT
我認爲問題出在我的配置或我的代碼庫中。爲此,我今天嘗試使用Spies而不是MockClasses。通過使用Spies,我希望消除因錯誤供應商而產生的複雜問題。系統在瀏覽器中測試時加載,所以我知道我創建的常規提供程序工作正常,沒有加載錯誤。
使用Spies並未解決問題。我仍然得到DecoratorFactory錯誤。我會繼續嘗試新的解決方案,直到找到答案。任何援助表示讚賞。
這個錯誤意味着你正在向NgModule中導入一些不是你可以導入的NgModule的東西。你可能已經知道這一點,但這就是錯誤所指的。它作爲裝飾工廠錯誤而表現的原因是Angular不直接使用裝飾器,而是使用裝飾器工廠,例如Input()而不是Input,這可能會讓人困惑,因爲它的文檔將這些引用爲裝飾器。我沒有在代碼中看到對DynamicTestModule的引用,因此它必須是傳遞依賴關係。 –
謝謝!實際上,我不知道。我是Angular的新手,所以我閱讀和聽到的幾乎所有東西似乎都是新的信息。一切都有用,謝謝! –