Angular Unit Testing with Jasmine - Error: Please add an @NgModule annotation
Angular Unit Testing with Jasmine - Error: Please add an @NgModule annotation
我正在尝试为使用两种服务和一种形式的 Angular 组件编写 Jasmine 单元测试(使用 Karma)。测试教程 (like this one from the Angular Docs) 仅展示了如何使用一项服务测试组件,而我无法以某种方式使其与更复杂的组件一起工作:
我的组件:用户-login.component.ts:
该组件有一个登录表单,用户可以在其中输入他的凭据。 OnSubmit
我将提供的凭据发送到身份验证服务,该服务处理对我的 API 的 http 请求。如果来自 API 的 http 响应的状态为 200,它将包含一个登录令牌 (JWT),我将其存储在另一个名为 TokenStorageService 的服务中:
import { Component, OnInit } from '@angular/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import { TokenStorageService } from '../../../_services/token-storage.service';
import { AuthenticationService } from '../../../_services/authentication.service';
import { AuthRequest } from '../../../_models/authRequest';
@Component({
selector: 'app-user-login',
templateUrl: './user-login.component.html',
styleUrls: ['./user-login.component.scss']
})
export class UserLoginComponent implements OnInit {
loginForm: FormGroup;
constructor(private formBuilder: FormBuilder,
private tokenStorage: TokenStorageService,
private authService: AuthenticationService) { }
ngOnInit() {
this.loginForm = this.formBuilder.group({
username: ['', Validators.compose([Validators.required])],
password: ['', Validators.required]
});
}
onSubmit() {
this.authService.login({
userName: this.loginForm.controls.username.value,
password: this.loginForm.controls.password.value
})
.subscribe(data => {
if (data.status === 200) {
this.tokenStorage.saveToken(data.body)
console.log("SUCCESS: logged in")
}
}
});
}
}
我的测试:用户-login.component.spec.ts:
所以我明白我在构造函数中提供的三样东西(FormBuilder
、TokenStorageService
和 AuthenticationService
)我也必须在我的 TestBed
中提供。因为我真的不想为单元测试注入服务,所以我改用存根服务。所以我这样做了:
TestBed.configureTestingModule({
imports: [{HttpClientTestingModule}],
providers: [{provide: FormBuilder}, { provide: TokenStorageService, useValue: tokenStorageServiceStub }, { provide: AuthenticationService, useValue: authenticationServiceStub }
整个测试看起来像这样:
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { UserLoginComponent } from './user-login.component';
import { FormBuilder } from '@angular/forms';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { TokenStorageService } from 'src/app/_services/token-storage.service';
import { AuthenticationService } from 'src/app/_services/authentication.service';
describe('UserLoginComponent', () => {
let component: UserLoginComponent;
let fixture: ComponentFixture<UserLoginComponent>;
let tokenStorageServiceStub: Partial<TokenStorageService>;
let authenticationServiceStub: Partial<AuthenticationService>;
// let tokenStorageService;
// let authenticationService;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [{HttpClientTestingModule}],
providers: [{provide: FormBuilder}, { provide: TokenStorageService, useValue: tokenStorageServiceStub }, { provide: AuthenticationService, useValue: authenticationServiceStub } ],
declarations: [ UserLoginComponent ]
})
fixture = TestBed.createComponent(UserLoginComponent);
component = fixture.componentInstance;
// tokenStorageService = TestBed.inject(TokenStorageService);
// authenticationService = TestBed.inject(AuthenticationService);
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
我评论了 4 行,因为我认为它们是错误的,但在 the Angular Docs example 他们也注入了真正的服务,甚至他们说他们不想在测试中使用真正的服务.我不明白文档示例中的那部分内容?
但无论哪种方式,我都会不断收到此错误消息:
因为错误说的是 @NgModule
,我想这可能与我的 app.module.ts
文件有关?这是我的 app.module.ts
:
@NgModule({
declarations: [
AppComponent,
SidebarComponent,
UsersComponent,
DetailsComponent,
ProductsComponent,
UploadFileComponent,
GoogleMapsComponent,
AddUserComponent,
ProductFormComponent,
UserLoginComponent,
EditUserComponent,
ProductDetailsComponent,
MessagesComponent,
MessageDetailsComponent,
ChatComponent,
UploadMultipleFilesComponent,
InfoWindowProductOverviewComponent,
AddDormComponent,
AddProductComponent
],
imports: [
BrowserModule,
AppRoutingModule,
HttpClientModule,
BrowserAnimationsModule,
FormsModule,
ReactiveFormsModule,
ImageCropperModule,
DeferLoadModule,
//Angular Material inputs (spezielle UI Elemente)
MatDatepickerModule,
MatInputModule,
MatNativeDateModule,
MatSliderModule,
MatSnackBarModule,
MatSelectModule,
MatCardModule,
MatTooltipModule,
MatChipsModule,
MatIconModule,
MatExpansionModule,
MDBBootstrapModule,
AgmCoreModule.forRoot({
apiKey: gmaps_environment.GMAPS_API_KEY
})
],
providers: [
UploadFileService,
{provide: MAT_DATE_LOCALE, useValue: 'de-DE'},
{provide:HTTP_INTERCEPTORS, useClass:BasicAuthHttpInterceptorService, multi:true},
],
bootstrap: [AppComponent],
})
export class AppModule { }
可能是因为它没有从您提到的路径获取文件。使用组件文件中提到的相同路径并尝试。
这种问题主要是因为文件路径错误,重复声明,未声明等
import { TokenStorageService } from '../../../_services/token-storage.service';
import { AuthenticationService } from '../../../_services/authentication.service';
能否请您从测试用例的 providers
数组中删除 FormBuilder
并改为导入 ReactiveFormsModule
。
TestBed.configureTestingModule({
imports: [HttpClientTestingModule, ReactiveFormsModule],
providers: [{ provide: TokenStorageService, useValue: tokenStorageServiceStub }, { provide: AuthenticationService, useValue: authenticationServiceStub }
注意:不要将导入元素括在大括号内。
现在您只声明了存根 tokenStorageServiceStub
和 authenticationServiceStub
,但您需要在提供它们之前对其进行初始化。类似的东西:
tokenStorageServiceStub = {
saveToken: () => {}
};
authenticationServiceStub = {
login: () => of({status: 200, body: {}})
}
此外,请考虑@PrincelsNinja 的建议。
您的 app.module.ts
文件与使用测试台无关。请检查组件的所有导入,将它们放入 .configureTestBed
-导入中,并特别注意指令。
我经常在缺少导入时遇到此错误。
providers: [
ReactiveFormsModule,
{ provide: TokenStorageService,
useClass: {saveToken: (data: any) => data} },
{ provide: AuthenticationService,
useClass: {
login: (loginDetails: any): Promise<any> => {
return {status: 200}}.toPromise(); } }
}
},
]
此外,从规范文件中删除 TokenStorageService 和 AuthenticationService 的导入,看看它们是否是导致 dynamictestingmodule 中第一个未定义错误的原因。
我正在尝试为使用两种服务和一种形式的 Angular 组件编写 Jasmine 单元测试(使用 Karma)。测试教程 (like this one from the Angular Docs) 仅展示了如何使用一项服务测试组件,而我无法以某种方式使其与更复杂的组件一起工作:
我的组件:用户-login.component.ts:
该组件有一个登录表单,用户可以在其中输入他的凭据。 OnSubmit
我将提供的凭据发送到身份验证服务,该服务处理对我的 API 的 http 请求。如果来自 API 的 http 响应的状态为 200,它将包含一个登录令牌 (JWT),我将其存储在另一个名为 TokenStorageService 的服务中:
import { Component, OnInit } from '@angular/core';
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
import { TokenStorageService } from '../../../_services/token-storage.service';
import { AuthenticationService } from '../../../_services/authentication.service';
import { AuthRequest } from '../../../_models/authRequest';
@Component({
selector: 'app-user-login',
templateUrl: './user-login.component.html',
styleUrls: ['./user-login.component.scss']
})
export class UserLoginComponent implements OnInit {
loginForm: FormGroup;
constructor(private formBuilder: FormBuilder,
private tokenStorage: TokenStorageService,
private authService: AuthenticationService) { }
ngOnInit() {
this.loginForm = this.formBuilder.group({
username: ['', Validators.compose([Validators.required])],
password: ['', Validators.required]
});
}
onSubmit() {
this.authService.login({
userName: this.loginForm.controls.username.value,
password: this.loginForm.controls.password.value
})
.subscribe(data => {
if (data.status === 200) {
this.tokenStorage.saveToken(data.body)
console.log("SUCCESS: logged in")
}
}
});
}
}
我的测试:用户-login.component.spec.ts:
所以我明白我在构造函数中提供的三样东西(FormBuilder
、TokenStorageService
和 AuthenticationService
)我也必须在我的 TestBed
中提供。因为我真的不想为单元测试注入服务,所以我改用存根服务。所以我这样做了:
TestBed.configureTestingModule({
imports: [{HttpClientTestingModule}],
providers: [{provide: FormBuilder}, { provide: TokenStorageService, useValue: tokenStorageServiceStub }, { provide: AuthenticationService, useValue: authenticationServiceStub }
整个测试看起来像这样:
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { UserLoginComponent } from './user-login.component';
import { FormBuilder } from '@angular/forms';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { TokenStorageService } from 'src/app/_services/token-storage.service';
import { AuthenticationService } from 'src/app/_services/authentication.service';
describe('UserLoginComponent', () => {
let component: UserLoginComponent;
let fixture: ComponentFixture<UserLoginComponent>;
let tokenStorageServiceStub: Partial<TokenStorageService>;
let authenticationServiceStub: Partial<AuthenticationService>;
// let tokenStorageService;
// let authenticationService;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [{HttpClientTestingModule}],
providers: [{provide: FormBuilder}, { provide: TokenStorageService, useValue: tokenStorageServiceStub }, { provide: AuthenticationService, useValue: authenticationServiceStub } ],
declarations: [ UserLoginComponent ]
})
fixture = TestBed.createComponent(UserLoginComponent);
component = fixture.componentInstance;
// tokenStorageService = TestBed.inject(TokenStorageService);
// authenticationService = TestBed.inject(AuthenticationService);
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
我评论了 4 行,因为我认为它们是错误的,但在 the Angular Docs example 他们也注入了真正的服务,甚至他们说他们不想在测试中使用真正的服务.我不明白文档示例中的那部分内容?
但无论哪种方式,我都会不断收到此错误消息:
因为错误说的是 @NgModule
,我想这可能与我的 app.module.ts
文件有关?这是我的 app.module.ts
:
@NgModule({
declarations: [
AppComponent,
SidebarComponent,
UsersComponent,
DetailsComponent,
ProductsComponent,
UploadFileComponent,
GoogleMapsComponent,
AddUserComponent,
ProductFormComponent,
UserLoginComponent,
EditUserComponent,
ProductDetailsComponent,
MessagesComponent,
MessageDetailsComponent,
ChatComponent,
UploadMultipleFilesComponent,
InfoWindowProductOverviewComponent,
AddDormComponent,
AddProductComponent
],
imports: [
BrowserModule,
AppRoutingModule,
HttpClientModule,
BrowserAnimationsModule,
FormsModule,
ReactiveFormsModule,
ImageCropperModule,
DeferLoadModule,
//Angular Material inputs (spezielle UI Elemente)
MatDatepickerModule,
MatInputModule,
MatNativeDateModule,
MatSliderModule,
MatSnackBarModule,
MatSelectModule,
MatCardModule,
MatTooltipModule,
MatChipsModule,
MatIconModule,
MatExpansionModule,
MDBBootstrapModule,
AgmCoreModule.forRoot({
apiKey: gmaps_environment.GMAPS_API_KEY
})
],
providers: [
UploadFileService,
{provide: MAT_DATE_LOCALE, useValue: 'de-DE'},
{provide:HTTP_INTERCEPTORS, useClass:BasicAuthHttpInterceptorService, multi:true},
],
bootstrap: [AppComponent],
})
export class AppModule { }
可能是因为它没有从您提到的路径获取文件。使用组件文件中提到的相同路径并尝试。 这种问题主要是因为文件路径错误,重复声明,未声明等
import { TokenStorageService } from '../../../_services/token-storage.service';
import { AuthenticationService } from '../../../_services/authentication.service';
能否请您从测试用例的 providers
数组中删除 FormBuilder
并改为导入 ReactiveFormsModule
。
TestBed.configureTestingModule({
imports: [HttpClientTestingModule, ReactiveFormsModule],
providers: [{ provide: TokenStorageService, useValue: tokenStorageServiceStub }, { provide: AuthenticationService, useValue: authenticationServiceStub }
注意:不要将导入元素括在大括号内。
现在您只声明了存根 tokenStorageServiceStub
和 authenticationServiceStub
,但您需要在提供它们之前对其进行初始化。类似的东西:
tokenStorageServiceStub = {
saveToken: () => {}
};
authenticationServiceStub = {
login: () => of({status: 200, body: {}})
}
此外,请考虑@PrincelsNinja 的建议。
您的 app.module.ts
文件与使用测试台无关。请检查组件的所有导入,将它们放入 .configureTestBed
-导入中,并特别注意指令。
我经常在缺少导入时遇到此错误。
providers: [
ReactiveFormsModule,
{ provide: TokenStorageService,
useClass: {saveToken: (data: any) => data} },
{ provide: AuthenticationService,
useClass: {
login: (loginDetails: any): Promise<any> => {
return {status: 200}}.toPromise(); } }
}
},
]
此外,从规范文件中删除 TokenStorageService 和 AuthenticationService 的导入,看看它们是否是导致 dynamictestingmodule 中第一个未定义错误的原因。