#angular #typescript #unit-testing #karma-jasmine
Вопрос:
У меня есть следующий компонент входа в систему.
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { BasicAuthService } from './../../services/auth/basic-auth.service';
import {ConfigService} from '../../services/common/config.service';
import { RefdataService } from '../../services/common/refdata.service';
import { config } from 'rxjs';
import { switchMap } from 'rxjs/operators';
import { RouteGuardService } from '../../services/auth/route-guard.service';
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.scss']
})
export class LoginComponent implements OnInit {
form: FormGroup;
private formSubmitAttempt: boolean;
invalidLogin = false;
credentials:any;
userPermissions: any;
errorMessage: any = null;
isLoading: boolean = false;
constructor(
private router: Router,
private fb: FormBuilder,
private basicAuthService: BasicAuthService,
public config: ConfigService,
private refdataService: RefdataService,
private authGuard: RouteGuardService,
) { }
ngOnInit(): void {
this.form = this.fb.group({
userName: ['', Validators.required],
password: ['', Validators.required]
});
}
handleAuthentication() {
if (this.form.valid) {
this.isLoading = true;
console.log("handleAuthentication");
let iUserName = this.form.value.userName;
let iPassword = this.form.value.password;
this.credentials = {
username: iUserName,
password: iPassword
}
this.basicAuthService.getAuthentication(this.credentials)
.pipe(
switchMap(
data => {
console.log(data);
this.invalidLogin = false;
this.formSubmitAttempt = true;
return this.refdataService.loadRefDataConfig();
})
).subscribe(
res => {
// some variable updates
this.router.navigate(['main']);
},
error => {
this.isLoading = false;
console.log(error.status);
let mess = '';
if (error.status === 401) {
mess = 'Invalid Login credentials supplied! Correct the errors and re-submit.'
} else {
mess = error.error
}
this.errorMessage = { _body: 'Error Code: ' error.status 'nMessage: ' mess };
this.errorService.openSnackBar(this.errorMessage, 'Close', 'red-snackbar', 10000);
this.invalidLogin = true;
console.log(this.invalidLogin);
}
);
}
}
}
Я хочу написать тест для этого компонента и буду имитировать все службы. Мне здесь нужна помощь, поскольку я новичок в Angular.
В принципе, мой тест (ниже) проваливается.
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { FormsModule } from '@angular/forms'
import { ReactiveFormsModule } from '@angular/forms';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { LoginComponent } from './login.component';
describe('LoginComponent', () => {
let component: LoginComponent;
let fixture: ComponentFixture<LoginComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
ReactiveFormsModule,
FormsModule,
HttpClientTestingModule
],
declarations: [ LoginComponent ],
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(LoginComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
Error:
LoginComponent > should create
TypeError: Cannot read property 'environmentName' of undefined
at <Jasmine>
at new BasicAuthService (http://localhost:0000/_karma_webpack_/src/app/services/auth/basic-auth.service.ts:24:49)
at Object.BasicAuthService_Factory [as factory] (ng:///BasicAuthService/ɵfac.js:5:10)
at R3Injector.hydrate (http://localhost:0000/_karma_webpack_/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:11248:1)
at R3Injector.get (http://localhost:0000/_karma_webpack_/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:11070:1)
at NgModuleRef$1.get (http://localhost:0000/_karma_webpack_/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:24198:1)
at Object.get (http://localhost:0000/_karma_webpack_/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:22101:1)
at getOrCreateInjectable (http://localhost:0000/_karma_webpack_/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:3921:1)
at ɵɵdirectiveInject (http://localhost:0000/_karma_webpack_/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:13752:1)
at NodeInjectorFactory.LoginComponent_Factory [as factory] (ng:///LoginComponent/ɵfac.js:6:50)
at getNodeInjectable (http://localhost:0000/_karma_webpack_/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:4029:1)
Expected undefined to be truthy.
Error: Expected undefined to be truthy.
at <Jasmine>
at UserContext.<anonymous> (http://localhost:0000/_karma_webpack_/src/app/components/login/login.component.spec.ts:35:23)
at ZoneDelegate.invoke (http://localhost:0000/_karma_webpack_/node_modules/zone.js/dist/zone-evergreen.js:364:1)
at ProxyZoneSpec.onInvoke (http://localhost:0000/_karma_webpack_/node_modules/zone.js/dist/zone-testing.js:292:1)
В принципе, это сбой из-за конструктора для службы basic-auth.
export class BasicAuthService {
environmentName = '';
environmentUrl = '';
constructor(
private http: HttpClient,
private config: ConfigService,
private runtimeConfig: RuntimeConfigService,
) {
this.environmentName = runtimeConfig.config.environmentName;
this.environmentUrl = this.environmentName == "localhost" ? "http://" runtimeConfig.config.serviceUrl : runtimeConfig.config.serviceUrl;
}
Я могу/должен издеваться над этой службой (нужна помощь здесь) и любыми другими службами, которые она вызывает.
Как я могу издеваться над BasicAuthService, чтобы его конструктор не запускался (ни другие службы внутри него не должны запускаться), просто издеваться над ним и использовать его в моем компонентном тесте?
Ответ №1:
Несколько советов:
- @Необязательно отлично подходит для игнорирования зависимостей. Если вы не собираетесь тестировать службу, выберите ее по желанию. Не вводите его и забудьте об этом.
- Для вещей, над которыми вы не можете издеваться, вместо этого
{ provide: BasicAuthService, useClass: BasicAuthServiceMock } export class BasicAuthServiceMock {}
- В качестве альтернативы от углового используйте jasmine spyOn, чтобы издеваться над зависимостями и просто использовать новый компонент LoginComponent. Немного неортодоксально, но у вас действительно могут быть прямые модульные тесты, такие как этот