Angular Unit Testing с помощью Jasmine - Ошибка: добавьте аннотацию @NgModule
Я пытаюсь написать Jasmine Unit Test (с Karma) для компонента Angular, который использует две службы и одну форму. Учебники по тестированию ( например, это из Angular Docs ) показывают только, как тестировать компонент с помощью одной службы, и почему-то я не могу заставить его работать с немного более сложным компонентом:
Мой компонент: user-login.component.ts:
Компонент имеет форму входа в систему, где пользователь может ввести свои учетные данные. OnSubmitЯ отправляю предоставленные учетные данные в службу аутентификации, которая обрабатывает http-запрос к моему API. Если HTTP-ответ от API имеет статус 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")
}
}
});
}
}
Мой тест: user-login.component.spec.ts:
Итак, я понял, что три вещи, которые я предоставляю в конструкторе ( FormBuilder, TokenStorageServiceи AuthenticationService), я также должен предоставить в моем TestBed. И поскольку я действительно не хочу внедрять сервисы для модульного теста, я вместо этого использую Stub Services. Итак, я сделал это:
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 строки, так как считаю, что они ошибочны, но в примере с Angular Docs они также внедряют реальные сервисы, даже если они жестко говорят, что не хотят использовать настоящие сервисы в тесте. Я не понимаю эту часть в примере с Документами?
Но в любом случае я продолжаю получать это сообщение об ошибке:
Поскольку ошибка говорит о чем-то, @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 { }
Ответы
Прямо сейчас вы объявили только заглушки tokenStorageServiceStubи authenticationServiceStub, но вам нужно инициализировать их, прежде чем предоставлять. Что-то в этом роде:
tokenStorageServiceStub = {
saveToken: () => {}
};
authenticationServiceStub = {
login: () => of({status: 200, body: {}})
}
Кроме того, примите во внимание рекомендацию @PrincelsNinja.
Не могли бы вы удалить FormBuilderиз providersмассива в ваших тестовых случаях и ReactiveFormsModuleвместо этого импортировать .
TestBed.configureTestingModule({
imports: [HttpClientTestingModule, ReactiveFormsModule],
providers: [{ provide: TokenStorageService, useValue: tokenStorageServiceStub }, { provide: AuthenticationService, useValue: authenticationServiceStub }
Примечание. Не заключайте элементы импорта в фигурные скобки.
Ваш app.module.tsфайл не имеет значения при использовании тестового стенда. Пожалуйста, проверьте весь импорт ваших компонентов, поместите их в свой .configureTestBed-import и будьте особенно осторожны с директивами.
Я довольно часто получаю эту ошибку, когда отсутствует импорт.
providers: [
ReactiveFormsModule,
{ provide: TokenStorageService,
useClass: {saveToken: (data: any) => data} },
{ provide: AuthenticationService,
useClass: {
login: (loginDetails: any): Promise<any> => {
return {status: 200}}.toPromise(); } }
}
},
]
Кроме того, удалите импорт для TokenStorageService и AuthenticationService из ваших файлов спецификаций, чтобы проверить, не вызывают ли они первую неопределенную ошибку в модуле динамического тестирования.
Это может быть из-за того, что он не получает файлы по указанному вами пути. Используйте тот же путь, который указан в файле компонента, и попробуйте. В основном такая проблема возникает из-за неправильного пути к файлу, повторяющихся объявлений, а не объявлений и т. Д.
import { TokenStorageService } from '../../../_services/token-storage.service';
import { AuthenticationService } from '../../../_services/authentication.service';