Компонент тестирования Angular 2 typescript jasmine с несколькими службами с конструкторами — макет всех служб и их конструкторов

#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. Немного неортодоксально, но у вас действительно могут быть прямые модульные тесты, такие как этот