Angular — функция внутри подписки никогда не вызывалась в модульном тестировании

#angular #typescript #unit-testing #jasmine #karma-jasmine

#angular #typescript #модульное тестирование #jasmine #карма-жасмин

Вопрос:

Я пытаюсь проверить, вызывается ли моя функция навигации для удаления транспортного средства. Но, похоже, он никогда не вызывается. Я думаю, что это может иметь какое-то отношение к тому факту, что это асинхронная функция, но даже когда я жду завершения полной подписки, она не работает.

.ts:

 deleteVehicle(): void {
    if (confirm('Bent u zeker dat u deze wagen wilt verwijderen?')) {
      this.apiService.deleteVehicle(this.selectedVehicle.id).subscribe(() => this.navigateToListVehicleComponent());
    }
  }
 

spec.ts:

 beforeEach(() => {
    fixture = TestBed.createComponent(VehicleDetailsComponent);
    component = fixture.componentInstance;

    apiService = TestBed.get(ApiService);
    fixture.detectChanges();
});
describe('#deleteVehicle', () => {
    it('should navigate to list vehicle component', fakeAsync(() => {
      spyOn(window, 'confirm').and.returnValue(true);
      spy = spyOn(apiService, 'deleteVehicle').withArgs(component.selectedVehicle.id).and.callThrough();
      let navigateSpy = spyOn(component,'navigateToListVehicleComponent').and.callThrough();

      component.deleteVehicle();
      tick();
      expect(spy).toHaveBeenCalled();
      expect(navigateSpy).toHaveBeenCalled();
    }));
  });
 

service.ts:

 private deleteFromAPI(url): any {
    return this.http
    .delete(this.BASE_API_URL   url)
    .pipe(catchError(this.handleError));
}
deleteVehicle(id: number): any {
    return this.deleteFromAPI('vehicle/'   id);
}

 

и в моем тестовом модуле я импортирую HttpClientTestingModule.

ошибка: ожидаемый вызов spy navigateToListVehicleComponent .

Кто-нибудь знает, что я делаю не так?

Комментарии:

1. Можете ли вы показать настройки для своих тестов? Вы издевались apiService.deleteVehicle над методом?

2. @slideshowp2 Я немного изменил код, надеюсь, его легче понять, что я делаю сейчас. Я в основном издеваюсь над своим apiService с помощью HttpClientTestingModule, а затем слежу за apiService. Метод deleteVehicle.

3. Вы используете callThrough() which делегирует фактическую реализацию. Это может вызвать проблему. Можете ли вы опубликовать фактическую реализацию apiService.deleteVehicle() метода?

4. Я только что отредактировал сообщение с реализацией apiService. deleteVehicle() надеюсь, это поможет

Ответ №1:

Моя стратегия тестирования заключается в том, чтобы заглушить apiService.deleteVehicle метод, чтобы он не имел побочного эффекта (отправка реального HTTP-запроса по сети).

решение для модульного тестирования с использованием angular v11 :

example.component.ts :

 import { Component } from '@angular/core';
import { ApiService } from './api.service';

@Component({})
export class ExampleComponent {
  selectedVehicle = {
    id: 1,
  };
  constructor(private apiService: ApiService) {}
  navigateToListVehicleComponent() {
    console.log('navigateToListVehicleComponent real implementation');
  }

  deleteVehicle(): void {
    if (confirm('Bent u zeker dat u deze wagen wilt verwijderen?')) {
      this.apiService
        .deleteVehicle(this.selectedVehicle.id)
        .subscribe(() => this.navigateToListVehicleComponent());
    }
  }
}
 

api.service.ts :

 import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { of } from 'rxjs';
import { catchError } from 'rxjs/operators';

@Injectable()
export class ApiService {
  BASE_API_URL = 'http://localhost';
  constructor(private http: HttpClient) {}

  private deleteFromAPI(url): any {
    return this.http
      .delete(this.BASE_API_URL   url)
      .pipe(catchError(this.handleError));
  }
  private handleError(err) {
    return of(err);
  }
  deleteVehicle(id: number): any {
    return this.deleteFromAPI('vehicle/'   id);
  }
}
 

example.component.spec.ts :

 import { HttpClient, HttpClientModule } from '@angular/common/http';
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { of } from 'rxjs';
import { ApiService } from './api.service';
import { ExampleComponent } from './example.component';

fdescribe('#deleteVehicle', () => {
  let apiService: ApiService;
  let fixture: ComponentFixture<ExampleComponent>;
  let component: ExampleComponent;

  beforeEach(
    waitForAsync(() => {
      TestBed.configureTestingModule({
        declarations: [ExampleComponent],
        imports: [HttpClientModule],
        providers: [ApiService, HttpClient],
      })
        .compileComponents()
        .then(() => {
          fixture = TestBed.createComponent(ExampleComponent);
          apiService = TestBed.get(ApiService);
          component = fixture.componentInstance;
          fixture.detectChanges();
        });
    })
  );
  it('should navigate to list vehicle component', () => {
    const confirmSpy = spyOn(window, 'confirm').and.returnValue(true);
    const deleteVehicleSpy = spyOn(apiService, 'deleteVehicle')
      .withArgs(component.selectedVehicle.id)
      .and.returnValue(of('deleteVehicle fake implementation'));

    let navigateSpy = spyOn(
      component,
      'navigateToListVehicleComponent'
    ).and.stub();

    component.deleteVehicle();

    expect(confirmSpy).toHaveBeenCalledOnceWith(
      'Bent u zeker dat u deze wagen wilt verwijderen?'
    );
    expect(deleteVehicleSpy).toHaveBeenCalledOnceWith(1);
    expect(navigateSpy).toHaveBeenCalled();
  });
});
 

результат модульного теста:

введите описание изображения здесь

Комментарии:

1. Большое вам спасибо, это исправлено!