Angular Unit Testing mit Jasmine - Fehler: Bitte fügen Sie eine @ NgModule-Annotation hinzu
Ich versuche, einen Jasmine Unit Test (mit Karma) für eine Angular-Komponente zu schreiben, die zwei Dienste und ein Formular verwendet. Die Tutorials zum Testen ( wie dieses aus den Angular Docs ) zeigen nur, wie eine Komponente mit einem Dienst getestet wird, und irgendwie kann ich sie nicht mit einer etwas komplexeren Komponente zum Laufen bringen:
Meine Komponente: user-login.component.ts:
Die Komponente verfügt über ein Anmeldeformular, in das der Benutzer seine Anmeldeinformationen eingeben kann. OnSubmitIch sende die angegebenen Anmeldeinformationen an einen Authentifizierungsdienst, der die http-Anforderung an meine API verarbeitet. Wenn die http-Antwort von der API den Status 200 hat, enthält sie ein Anmeldetoken (JWT), das ich bei einem anderen Dienst speichere, den ich TokenStorageService genannt habe:
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")
}
}
});
}
}
Mein Test: user-login.component.spec.ts:
So habe ich verstanden , dass die drei Dinge , die ich in dem Konstruktor bieten ( FormBuilder, TokenStorageServiceund AuthenticationService) Ich habe auch in meiner zu schaffen TestBed. Und da ich die Services für den Unit-Test nicht wirklich einbinden möchte, verwende ich stattdessen Stub Services. Also habe ich das gemacht:
TestBed.configureTestingModule({
imports: [{HttpClientTestingModule}],
providers: [{provide: FormBuilder}, { provide: TokenStorageService, useValue: tokenStorageServiceStub }, { provide: AuthenticationService, useValue: authenticationServiceStub }
Der ganze Test sieht dann so aus:
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();
});
});
Ich habe 4 Zeilen auskommentiert, da ich denke, dass sie falsch sind, aber im Angular Docs-Beispiel injizieren sie auch die realen Dienste, selbst wenn sie hart sagen, dass sie die realen Dienste im Test nicht verwenden wollen. Ich verstehe diesen Teil im Docs-Beispiel nicht?
Aber so oder so bekomme ich immer wieder diese Fehlermeldung:
Da der Fehler etwas darüber aussagt, @NgModuledenke ich, dass er möglicherweise mit meiner app.module.tsDatei zu tun hat ? Hier ist mein 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 { }
Antworten
Im Moment haben Sie nur die Stubs tokenStorageServiceStubund deklariert authenticationServiceStub, aber Sie müssen sie initialisieren, bevor Sie sie bereitstellen. Etwas in diese Richtung:
tokenStorageServiceStub = {
saveToken: () => {}
};
authenticationServiceStub = {
login: () => of({status: 200, body: {}})
}
Berücksichtigen Sie außerdem die Empfehlung von @PrincelsNinja.
Können Sie das bitte in Ihren Testfällen FormBuilderaus dem providersArray entfernen und ReactiveFormsModulestattdessen importieren ?
TestBed.configureTestingModule({
imports: [HttpClientTestingModule, ReactiveFormsModule],
providers: [{ provide: TokenStorageService, useValue: tokenStorageServiceStub }, { provide: AuthenticationService, useValue: authenticationServiceStub }
Hinweis: Schließen Sie die Importelemente nicht in geschweifte Klammern ein.
Ihre app.module.tsDatei ist bei Verwendung des Prüfstands irrelevant. Bitte überprüfen Sie alle Importe Ihrer Komponenten, legen Sie sie in Ihren .configureTestBedImport und gehen Sie besonders vorsichtig mit Anweisungen um.
Ich erhalte diesen Fehler ziemlich oft, wenn ein Import fehlt.
providers: [
ReactiveFormsModule,
{ provide: TokenStorageService,
useClass: {saveToken: (data: any) => data} },
{ provide: AuthenticationService,
useClass: {
login: (loginDetails: any): Promise<any> => {
return {status: 200}}.toPromise(); } }
}
},
]
Entfernen Sie außerdem die Importe für TokenStorageService und AuthenticationService aus Ihren Spezifikationsdateien, um festzustellen, ob diese den ersten undefinierten Fehler im dynamictestingmodule verursachen.
Dies kann daran liegen, dass die Dateien nicht von dem von Ihnen angegebenen Pfad abgerufen werden. Verwenden Sie denselben Pfad wie in der Komponentendatei angegeben und versuchen Sie es. Meistens ist dieses Problem auf einen falschen Dateipfad, doppelte Deklarationen, keine Deklarationen usw. zurückzuführen.
import { TokenStorageService } from '../../../_services/token-storage.service';
import { AuthenticationService } from '../../../_services/authentication.service';