需要有关Angular2 TestBed配置的帮助。 DecoratorFactory错误

时间:2017-02-09 16:57:00

标签: angular typescript karma-jasmine angular2-testing

我正在为名为' DashboardComponent'的组件创建测试。测试规范很难创建。 Karma正在抛出错误"意外的价值' DecoratorFactory'由模块' DynamicTestModule'"。

导入

我尝试删除代码,直到错误消失,然后再添加代码,直到它重新出现以识别源代码。但是,这没有提供富有成效的结果,因为在使用多个不同的配置时代码中断了,我无法分辨哪个配置导致错误存在,哪个导致错误被抛出。我怀疑在我的所有非失败尝试中都存在错误的配置,并且某些代码行(例如添加it()调用)导致错误在之前不可见时突然可见。

我希望有比我更多经验的人会看到我的错误并提供一些建议。其他人在StackOverflow上遇到了同样的问题,并且适合他们的解决方案似乎不适用于这种情况。

代码图

为了使代码库更容易消化,我创建了一个UML图来显示我试图在TestBed配置中复制的依赖项。

enter image description here

绿色元素是被测试的元素。 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错误。我将继续尝试新的解决方案,直到找到答案。任何帮助表示赞赏。

1 个答案:

答案 0 :(得分:2)

看起来问题很可能是由这条线引起的。即使不是,也可能是错误

imports: [ NgModule, RouterModule, FormsModule ],

请注意,NgModule 是一个Angular 2模块,而是一个装饰工厂,它返回一个已配置的装饰器,该装饰器又将目标类表示为Angular 2模块。将其作为Angular 2模块导入可能会引发此错误。