我有一个用Angular制作的Sidenav菜单。该组件如下所示:
模板:
<mat-toolbar color="primary" [fxLayoutAlign]="(settings.menuType != 'mini') ? 'space-between center' : 'center center'" class="sidenav-header">
<a mat-raised-button color="accent" routerLink="/" (click)="closeSubMenus()" class="small-logo">Menu</a>
<a *ngIf="settings.menuType == 'default'" class="logo" routerLink="/" (click)="closeSubMenus()">mEMS</a>
</mat-toolbar>
<div fxLayout="column" fxLayoutAlign="center center" class="user-block transition-2" [class.show]="settings.sidenavUserBlock">
<div [fxLayout]="(settings.menuType != 'default') ? 'column' : 'row'"
[fxLayoutAlign]="(settings.menuType != 'default') ? 'center center' : 'space-around center'" class="user-info-wrapper">
<div class="user-info">
<p class="name"> {{currentUser}}</p>
<p *ngIf="settings.menuType == 'default'" class="position">Rocket Scientist<br> </p>
</div>
</div>
<div *ngIf="settings.menuType != 'mini'" fxLayout="row" fxLayoutAlign="space-around center" class="w-100 muted-text">
<a mat-icon-button (click)="logout();" routerLink="/login">
<mat-icon>power_settings_new</mat-icon>
</a>
</div>
</div>
<div id="sidenav-menu-outer" class="sidenav-menu-outer" perfectScrollbar [class.user-block-show]="settings.sidenavUserBlock">
<span *ngIf="!menuItems">loading....</span>
<app-vertical-menu [menuItems]="menuItems" [menuParentId]="0"></app-vertical-menu>
</div>
实际组件:
import { Component, OnInit, ViewEncapsulation } from '@angular/core';
import { AppSettings } from '../../../app.settings';
import { Settings } from '../../../app.settings.model';
import { MenuService } from '../menu/menu.service';
import { AuthService } from '../../../auth.service';
import { Router, ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-sidenav',
templateUrl: './sidenav.component.html',
styleUrls: ['./sidenav.component.scss'],
encapsulation: ViewEncapsulation.None,
providers: [ MenuService ]
})
export class SidenavComponent implements OnInit {
currentUser: String;
public userImage= '../assets/img/users/user.jpg';
public menuItems:Array<any>;
public settings: Settings;
constructor(public authService: AuthService, public appSettings:AppSettings, public menuService:MenuService,public router:Router,){
this.settings = this.appSettings.settings;
}
logout() {
this.authService.logout();
}
ngOnInit() {
let jwt = localStorage.getItem(AuthService.USER_TOKEN_KEY);
let jwtData = jwt.split('.')[1]
let decodedJwtJsonData = window.atob(jwtData)
let decodedJwtData = JSON.parse(decodedJwtJsonData)
console.log(decodedJwtData);
this.currentUser = decodedJwtData.sub;
this.menuItems = this.menuService.getVerticalMenuItems();
}
}
我的测试是默认的基本测试:
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { SidenavComponent } from './sidenav.component';
import { MatButtonModule, MatFormFieldModule, MatInputModule, MatRippleModule, MatCardModule, MatSidenavModule, MatToolbarModule, MatIconModule } from '@angular/material';
describe('SidenavComponent', () => {
let component: SidenavComponent;
let fixture: ComponentFixture<SidenavComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ SidenavComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(SidenavComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
每次运行此测试时,由于材料组件
,我都会收到这些错误Can't bind to 'fxLayoutAlign' since it isn't a known property of 'mat-toolbar'.
1. If 'mat-toolbar' is an Angular component and it has 'fxLayoutAlign' input, then verify that it is part of this module.
2. If 'mat-toolbar' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.
1. If 'app-vertical-menu' is an Angular component, then verify that it is part of this module.
2. If 'app-vertical-menu' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message. ("-block-show]="settings.sidenavUserBlock">
<span *ngIf="!menuItems">loading....</span>
我已添加到app.module.ts
中的所有上述组合import { MatButtonModule, MatFormFieldModule, MatInputModule, MatRippleModule, MatCardModule, MatSidenavModule, MatToolbarModule, MatIconModule } from '@angular/material';
并导出它们...所以我有点迷失,如果我不同地导入/导出这些模块,我不明白我做错了什么。
答案 0 :(得分:3)
错误很明显,您的组件不理解flexLayout
指令,因为您没有在测试模块中导入它。
import { FlexLayoutModule } from '@angular/flex-layout';
您缺少的是对测试模块的理解。基本上,它用于重新创建组件的上下文,以便能够将Angular在后台使用的整个依赖项放在一边。 这就是您需要重新导入原始模块中使用的模块的原因,这些模块声明了您的组件。
例如,这是我的测试模块之一:
TestBed.configureTestingModule({
imports: [
CommonModule,
BrowserAnimationsModule,
ReactiveFormsModule,
MaterialModule,
FlexLayoutModule,
RouterTestingModule,
],
declarations: [
SomeComponent
],
providers: [
// Some examples of stubs used
{ provide: SomeService, useClass: SomeServiceStub },
{ provide: MatDialogRef, useValue: {} },
{ provide: ActivatedRoute, useValue: { 'params': Observable.from([{ 'id': 1 }]) } }
],
schemas: [
NO_ERRORS_SCHEMA
]
});
您的第二个错误是因为您的组件可能使用app-vertical-menu
组件,并且由于您不希望在单元测试中使用该组件,因此您应该在测试模块中使用NO_ERRORS_SCHEMA
声明。
如果您想编写更复杂的测试,例如集成测试,您可以使用另一个测试模块定义另一个测试套件,它将同时包含两个组件(您必须同时声明这两个)。
以下是一个例子:
describe('Integration Test of SomeComponent', () => {
// fixtures and stuff declaration
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
CommonModule,
BrowserAnimationsModule,
ReactiveFormsModule,
MaterialModule,
FlexLayoutModule,
RouterTestingModule,
],
declarations: [
SomeComponent,
OtherComponent // Here the other one is declared
],
providers: [
{ provide: SomeService, useClass: SomeServiceStub }, {
provide: MatDialogRef, useValue: {}
}
]
// No more NO_ERROR_SCHEMA here
});
fixture = TestBed.createComponent(SomeComponent);
componentInstance = fixture.componentInstance;
fixture.detectChanges();
});
it('should create component', () => {
expect(componentInstance).toBeTruthy();
});
})
希望这有帮助。
编辑:以下是使用ServiceStub而非服务的组件示例。而不是实际调用 getTheme()并执行HTTP调用,我使用覆盖此方法的存根并返回静态数据。这允许避免使用Angular HTTP依赖项的完整服务,该依赖项也可能具有其他内部Angular Dependencies等。
// Outside of your test class
export class SomeServiceStub {
constructor() {}
getTheme(): Observable<Theme[]> {
const themes: Theme[] = [
{
id: 128,
code: 'EEE',
libelle: 'Fake label 1'
}, {
id: 119,
code: 'DDD',
libelle: 'Fake label 2'
}
];
return Observable.of(themes);
}
// Other services methods to override...
}
// In your testing module :
providers: [
{ provide: SomeService, useClass: SomeServiceStub },
{ provide: MatDialogRef, useValue: {} }
]