【问题标题】:Angular testing with Jasmine and Karma - router-outlet errors and component creation test fail, for lazy-loaded component使用 Jasmine 和 Karma 进行角度测试 - 延迟加载组件的路由器出口错误和组件创建测试失败
【发布时间】:2019-09-03 07:29:30
【问题描述】:
  • 我有一个可以成功构建和运行的 Angular 应用
  • 我正在使用ng test 运行测试
  • 我在创建所有组件时都遇到错误
  • 我在控制台中看到很多错误提示无法加载router-outlet
  • 我是 Angular 测试新手,所以任何关于我可以尝试/查看的内容可能会有所帮助

更新 - @Sanket 的回答很有用,但我仍然有这个问题。我已经评论了他的回答,以解释剩下的问题


BackupComponent 的加载有关的错误

(我认为 router-outlet 是 Angular 组件,而不是 Web 组件,因为代码库没有引用 CUSTOM_ELEMENTS_SCHEMA

        <div class="col-lg-7 col-xs-12">
    1. If 'router-outlet' is an Angular component, then verify that it is part of this module.
    2. If 'router-outlet' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message. ("
        </div>
        <div class="col-lg-7 col-xs-12">
          [ERROR ->]<router-outlet></router-outlet>
        </div>
      </div>
    Error: Template parse errors:
    'app-backup-list' is not a known element:
    1. If 'app-backup-list' is an Angular component, then verify that it is part of this module.
    2. If 'app-backup-list' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message. ("<div class="row">
        <div class="col-lg-5 col-xs-12">
            [ERROR ->]<app-backup-list></app-backup-list>
        </div>
        <div class="col-lg-7 col-xs-12">
    "): ng:///DynamicTestModule/BackupComponent.html@2:8
    'router-outlet' is not a known element:
    1. If 'router-outlet' is an Angular component, then verify that it is part of this module.
    2. If 'router-outlet' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message. ("
        </div>
        <div class="col-lg-7 col-xs-12">
          [ERROR ->]<router-outlet></router-outlet>
        </div>
      </div>
    "): ng:///DynamicTestModule/BackupComponent.html@5:6
    error properties: Object({ ngSyntaxError: true, ngParseErrors: [ 'app-backup-list' is not a known element:
    "): ng:///DynamicTestModule/BackupComponent.html@2:8, 'router-outlet' is not a known element:
    "): ng:///DynamicTestModule/BackupComponent.html@5:6 ] })
        at syntaxError (node_modules/@angular/compiler/fesm5/compiler.js:2430:1)
        at TemplateParser.push../node_modules/@angular/compiler/fesm5/compiler.js.TemplateParser.parse (node_modules/@angular/compiler/fesm5/compiler.js:20605:1)
        at JitCompiler.push../node_modules/@angular/compiler/fesm5/compiler.js.JitCompiler._parseTemplate (node_modules/@angular/compiler/fesm5/compiler.js:26171:1)
        at JitCompiler.push../node_modules/@angular/compiler/fesm5/compiler.js.JitCompiler._compileTemplate (node_modules/@angular/compiler/fesm5/compiler.js:26158:1)
        at node_modules/@angular/compiler/fesm5/compiler.js:26101:48
        at <Jasmine>
        at JitCompiler.push../node_modules/@angular/compiler/fesm5/compiler.js.JitCompiler._compileComponents (node_modules/@angular/compiler/fesm5/compiler.js:26101:1)
        at node_modules/@angular/compiler/fesm5/compiler.js:26019:1
        at Object.then (node_modules/@angular/compiler/fesm5/compiler.js:2421:33)
        at JitCompiler.push../node_modules/@angular/compiler/fesm5/compiler.js.JitCompiler._compileModuleAndAllComponents (node_modules/@angular/compiler/fesm5/compiler.js:26017:1)
    Error: Expected undefined to be truthy.
        at <Jasmine>
        at UserContext.<anonymous> (src/app/backup/components/backup/backup.component.spec.ts:23:23)
        at ZoneDelegate../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (node_modules/zone.js/dist/zone.js:391:1)

backup.component.spec.ts

import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { BackupComponent } from './backup.component';

describe('BackupComponent', () => {
  let component: BackupComponent;
  let fixture: ComponentFixture<BackupComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ BackupComponent ]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(BackupComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should be created', () => {
    expect(component).toBeTruthy();
  });
});

backup.component.html

<div class="row">
    <div class="col-lg-5 col-xs-12">
        <app-backup-list></app-backup-list>
    </div>
    <div class="col-lg-7 col-xs-12">
      <router-outlet></router-outlet>
    </div>
  </div>

backup.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'app-backup',
  templateUrl: './backup.component.html',
  styleUrls: ['./backup.component.css']
})
export class BackupComponent {

  constructor() { }

}

backup.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ReactiveFormsModule, FormsModule } from '@angular/forms';
import { AccordionModule } from 'ngx-bootstrap';

import { BackupRoutingModule } from './backup-routing.module';
import { BackupComponent } from './components/backup/backup.component';
import { BackupEditComponent } from './components/backup-edit/backup-edit.component';
import { BackupItemComponent } from './components/backup-item/backup-item.component';
import { BackupListComponent } from './components/backup-list/backup-list.component';
import { SharedModule } from '../shared/shared.module';
import { CommonAngularWidgetsLibModule } from '@ecotech/common-angular-widgets-lib';

@NgModule({
  declarations: [
    BackupComponent,
    BackupEditComponent,
    BackupItemComponent,
    BackupListComponent
  ],
  imports: [
    CommonModule,
    FormsModule,
    ReactiveFormsModule,
    AccordionModule,
    SharedModule,
    CommonAngularWidgetsLibModule,
    BackupRoutingModule
  ]
})
export class BackupModule { }

backup.routing.module.ts

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

import { BackupComponent } from './components/backup/backup.component';
import { AuthGuard } from '../guards/auth.guard';
import { BackupEditComponent } from './components/backup-edit/backup-edit.component';
import { PendingChangesGuard } from '../guards/pending-changes.guard';

const routes: Routes = [{
  path: '',
  component: BackupComponent,
  canActivate: [AuthGuard],
  children: [{
    path: 'new',
    component: BackupEditComponent,
    canDeactivate: [PendingChangesGuard]
  }, {
    path: ':id',
    component: BackupEditComponent,
    canDeactivate: [PendingChangesGuard]
  }]
}];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})
export class BackupRoutingModule { }

app.module.ts(缩减)

declare var require: any;

import { BrowserModule } from '@angular/platform-browser';
import { NgModule, LOCALE_ID, TRANSLATIONS, TRANSLATIONS_FORMAT } from '@angular/core';
import { ReactiveFormsModule, FormsModule  } from '@angular/forms';
import { DynamicFormModule } from './dynamic-form/dynamic-form.module';
import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';
import { SharedModule } from './shared/shared.module';
import { CoreModule } from './core/core.module';


@NgModule({
  declarations: [
    AppComponent,
    ... custom components, not including BackupComponent
  ],
  imports: [
    FormsModule,
    BrowserModule,
    ReactiveFormsModule,
    DynamicFormModule,
    CoreModule,
    SharedModule,
    AppRoutingModule,
  ],
  providers:
  [
    ... providers for app internationalisation only
  ],
  bootstrap: [AppComponent],
  entryComponents: [ConfirmationComponent,
    CalibrationWidgetComponent,
    RunCalibrationPointModalComponent,
    ... other custom components
  ]
})

export class AppModule { }

app.routing.module.ts (cut down) - 负责BackupModule的延迟加载

import { NgModule } from '@angular/core';
import { Routes, RouterModule, PreloadAllModules } from '@angular/router';


const appRoutes:  Routes = [
  { path: '', redirectTo: '/dashboard', pathMatch: 'full' },
  ...
  { path: 'backups', loadChildren: './backup/backup.module#BackupModule' },
  ... other custom routes
];

@NgModule({
  imports: [RouterModule.forRoot(appRoutes, { preloadingStrategy: PreloadAllModules })],
  exports: [RouterModule]
})

export class AppRoutingModule { }

【问题讨论】:

    标签: javascript angular jasmine karma-runner angular-router


    【解决方案1】:

    您需要在导入中包含 RouterTesting 模块,并在声明中包含 AppBackupListComponent。

    以下是您的测试的示例代码backup.component.spec.ts

    import { async, ComponentFixture, TestBed } from '@angular/core/testing';
    import { RouterTestingModule } from '@angular/router/testing';
    
    import { BackupComponent } from './backup.component';
    
    describe('BackupComponent', () => {
      let component: BackupComponent;
      let fixture: ComponentFixture<BackupComponent>;
    
      beforeEach(async(() => {
        TestBed.configureTestingModule({
          declarations: [ BackupComponent, BackupListComponent ],
          imports: [RouterTestingModule]
        })
        .compileComponents();
      }));
    
      beforeEach(() => {
        fixture = TestBed.createComponent(BackupComponent);
        component = fixture.componentInstance;
        fixture.detectChanges();
      });
    
      it('should be created', () => {
        expect(component).toBeTruthy();
      });
    });
    

    希望这会有所帮助。

    【讨论】:

    • 感谢@Sanket。我仍然收到 If 'router-outlet' is an Angular component, then verify that it is part of this module. 的错误,并且 import { RouterTestingModule } from '@angular/router/testing'; 行在 VS Code 中被标记为未使用的导入 - 我认为这很重要
    • 您是否在导入中包含了 RouterTestingModule ?如果包含,则不应显示为未使用的导入
    • 不,不在邮件应用模块中。您应该在此组件中包含 Testbed 导入。在导入中检查我上面的代码。
    猜你喜欢
    • 1970-01-01
    • 2020-12-07
    • 2020-05-30
    • 2015-03-23
    • 1970-01-01
    • 2021-04-09
    • 2020-04-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多