我正在为名为' DashboardComponent'的组件创建测试。测试规范很难创建。 Karma正在抛出错误"意外的价值' DecoratorFactory'由模块' DynamicTestModule'"。
导入我尝试删除代码,直到错误消失,然后再添加代码,直到它重新出现以识别源代码。但是,这没有提供富有成效的结果,因为在使用多个不同的配置时代码中断了,我无法分辨哪个配置导致错误存在,哪个导致错误被抛出。我怀疑在我的所有非失败尝试中都存在错误的配置,并且某些代码行(例如添加it()
调用)导致错误在之前不可见时突然可见。
我希望有比我更多经验的人会看到我的错误并提供一些建议。其他人在StackOverflow上遇到了同样的问题,并且适合他们的解决方案似乎不适用于这种情况。
代码图
为了使代码库更容易消化,我创建了一个UML图来显示我试图在TestBed配置中复制的依赖项。
绿色元素是被测试的元素。 package元素被导入到模块中,每个服务都有一个mock类,我们提供了mock类。我们必须声明三个组件,因为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
我认为问题出在我的配置或代码库中。出于这个原因,我今天尝试使用间谍而不是MockClasses。通过使用间谍,我希望消除由于有错误的提供者而产生的复杂性。系统在浏览器中进行测试时加载,因此我知道我创建的常规提供程序运行良好且没有加载错误。
使用间谍并没有解决问题。我仍然得到DecoratorFactory错误。我将继续尝试新的解决方案,直到找到答案。任何帮助表示赞赏。
答案 0 :(得分:2)
看起来问题很可能是由这条线引起的。即使不是,也可能是错误
imports: [ NgModule, RouterModule, FormsModule ],
请注意,NgModule
不 是一个Angular 2模块,而是一个装饰工厂,它返回一个已配置的装饰器,该装饰器又将目标类表示为Angular 2模块。将其作为Angular 2模块导入可能会引发此错误。