Проблема тестирования сервиса Angular с зависимостями абстрактных классов - PullRequest
0 голосов
/ 17 апреля 2020

У меня есть служба, для которой требуется зависимость от другой службы, которая, в свою очередь, требует зависимости от двух абстрактных классов.

(ThemeConfigService -> (SettingsService -> SettingsLoader, NavigationLoader))

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

Я не уверен, как мне это пройти, различные онлайн-поиски не оказались очень полезными.

Вот служба настройки темы, для которой я пытаюсь выполнить sh тест "theme-config.service.ts"

@Injectable({
  providedIn: 'root'
})
export class ThemeConfigService {

  constructor(
    private platform: Platform,
    private router: Router,
    private settings: SettingsService
  ) {
    // code removed for brevity
  }
}

Вот тестируемая служба "settings.service.ts"

@Injectable()
export class SettingsService {

  constructor(public settingsLoader: SettingsLoader,
              public navigationLoader: NavigationLoader) { }

  public settings(): Observable<any> {
    return this.settingsLoader.retrieveSettings();
  }

  public navigation(): Observable<any> {
    return this.navigationLoader.retrieveNavigation();
  }
}

Вот класс SettingsLoader, NavigationLoader выглядит точно так же. Они должны быть отдельными классами с точки зрения дизайна:

export abstract class SettingsLoader {
    abstract retrieveSettings(): Observable<any>;
}

Мой модульный тест выглядит примерно так:

describe('ThemeConfigService', () => {
  let service: ThemeConfigService;
  let router: Router;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [
        RouterTestingModule.withRoutes([])
      ],
      providers: [
        Platform,
        SettingsService,
        SettingsLoader,
        NavigationLoader
      ]
    });

    router = TestBed.inject(Router);
    service = TestBed.inject(ThemeConfigService);
  });

  it('should be created', async(inject([Platform, Router, SettingsService, SettingsLoader, NavigationLoader],
    (platform: Platform, router: Router, settings: SettingsService, settingsLoader: SettingsLoader, navigationLoader: NavigationLoader) => {

    expect(service).toBeTruthy();
  })));
});

Ошибка, возвращаемая Кармой:

TypeError: this.settingsLoader.retrieveSettings is not a function
which to me proves that it cannot resolve the abstract classes.

По этим причинам я пошел вперед и создал что-то вроде этого:

export class SettingsFakeLoader extends SettingsLoader {
    retrieveSettings(): Observable<any> {
        return of({});
    }
}

И попытался изменить инъекцию классов SettingsLoader и NavigationLoader с помощью них, тогда Карма отвечает:

NullInjectorError: R3InjectorError(DynamicTestModule)[ThemeConfigService -> SettingsService -> SettingsLoader -> SettingsLoader]: 
  NullInjectorError: No provider for SettingsLoader!

Измененный beforeEach для файла theme-config.service.spec.ts:

beforeEach(() => {
    TestBed.configureTestingModule({
        imports: [
        RouterModule,
        RouterTestingModule.withRoutes([])
        ],
        providers: [
        Platform,
        SettingsService,
        SettingsFakeLoader,
        NavigationFakeLoader
        ]
    });

    router = TestBed.inject(Router);
    service = TestBed.inject(ThemeConfigService);
});

Обычно я бы не пытался протестировать то, что мне кажется сложным. Может быть, я просто не вижу «решения».

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

1 Ответ

0 голосов
/ 17 апреля 2020

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

Обновленный describe для файла theme-config.service.spe.ts:

describe('ThemeConfigService', () => {
  let service: ThemeConfigService;
  let sLoader: SettingsLoader;
  let nLoader: NavigationLoader;
  let sService: SettingsService;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [
        RouterTestingModule.withRoutes([])
      ],
      providers: [
        ThemeConfigService,
        SettingsService,
        SettingsLoader,
        NavigationLoader
      ]
    });

    let platform = TestBed.inject(Platform);
    let router = TestBed.inject(Router);

    sLoader = new SettingsFakeLoader();
    nLoader = new NavigationFakeLoader();

    sService = new SettingsService(sLoader, nLoader);
    service = new ThemeConfigService(platform, router, sService);
  });

  it('should be created', () => {
    expect(service).toBeTruthy();
  });
});
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...