Тестирование - не удается разрешить все параметры для (ClassName)

контекст

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

ApiServiceподпись конструктора:

constructor(metaManager: MetaManager, connector: ApiConnectorService, eventDispatcher: EventDispatcher);
  • MetaManager это инъекционный сервис, который обрабатывает метаданные API.
  • ApiConnectorService это сервис, который упаковывает Http добавить наши собственные заголовки и систему подписи.
  • EventDispatcher это в основном система диспетчеризации событий Symfony, в машинописном тексте.

проблема

Когда я проверяю ApiServiceЯ делаю инициализацию в beforeEach:

beforeEach(async(() => {
    TestBed.configureTestingModule({
        imports  : [
            HttpModule
        ],
        providers: [
            ApiConnectorService,
            ApiService,
            MetaManager,
            EventDispatcher,
            OFF_LOGGER_PROVIDERS
        ]
    });
}));

и работает нормально.

Затем я добавляю свой второй файл спецификации, который предназначен для ApiConnectorService, с этим beforeEach:

beforeEach(async(() => {
    TestBed.configureTestingModule({
        imports  : [HttpModule],
        providers: [
            ApiConnectorService,
            OFF_LOGGER_PROVIDERS,
            AuthenticationManager,
            EventDispatcher
        ]
    });
}));

И все тесты терпят неудачу с этой ошибкой:

Ошибка: не удается разрешить все параметры для ApiService: (MetaManager,?, EventDispatcher).

  • Если я удалю api-connector-service.spec.ts (ApiConnectorServiceСпец файл) из моих загруженных тестов, ApiServiceИспытания пройдут успешно.
  • Если я удалю api-service.spec.ts (ApiServiceСпец файл) из моих загруженных тестов, ApiConnectorServiceИспытания пройдут успешно.

Почему у меня есть эта ошибка? Кажется, что контекст между моими двумя файлами конфликтует, и я не знаю, почему и как это исправить.

6 ответов

Решение

Это потому что Http услуга не может быть решена из HttpModule в тестовой среде. Это зависит от браузера платформы. Вы не должны даже пытаться делать звонки XHR во время тестов.

По этой причине Angular предоставляет MockBackend для Http Сервис для использования. Мы используем этот фиктивный бэкэнд для подписки на соединения в наших тестах, и мы можем имитировать ответ при каждом соединении.

Вот короткий полный пример, с которым вы можете работать

import { Injectable } from '@angular/core';
import { async, inject, TestBed } from '@angular/core/testing';
import { MockBackend, MockConnection } from '@angular/http/testing';
import {
  Http, HttpModule, XHRBackend, ResponseOptions,
  Response, BaseRequestOptions
} from '@angular/http';

@Injectable()
class SomeService {
  constructor(private _http: Http) {}

  getSomething(url) {
 return this._http.get(url).map(res => res.text());
  }
}

describe('service: SomeService', () => {
  beforeEach(() => {
 TestBed.configureTestingModule({
   providers: [
  {
    provide: Http, useFactory: (backend, options) => {
   return new Http(backend, options);
    },
    deps: [MockBackend, BaseRequestOptions]
  },
  MockBackend,
  BaseRequestOptions,
  SomeService
   ]
 });
  });

  it('should get value',
 async(inject([SomeService, MockBackend],
     (service: SomeService, backend: MockBackend) => {

 backend.connections.subscribe((conn: MockConnection) => {
   const options: ResponseOptions = new ResponseOptions({body: 'hello'});
   conn.mockRespond(new Response(options));
 });

 service.getSomething('http://dummy.com').subscribe(res => {
   console.log('subcription called');
   expect(res).toEqual('hello');
 });
  })));
});

Используете Jest?

В случае, если кто-то попадет сюда, и вы используете Jest для тестирования своего приложения Angular (надеюсь, мы - растущее меньшинство), вы столкнетесь с этой ошибкой, если не испускаете декораторы. Вам нужно будет обновить свойtsconfig.spec.json файл, чтобы он выглядел так:

{
  "extends": "../../tsconfig.json",
  "compilerOptions": {
    "emitDecoratorMetadata": true,
    "outDir": "../../out-tsc/spec",
    "types": [
      "jest",
      "node"
    ]
  },
  "files": [
  ],
  "include": [
    "**/*.spec.ts",
    "**/*.d.ts"
  ]
}

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

  1. Решение проблемы ОП:

Если у вас есть бочка (index.ts или мультиэкспортный файл), например так:

export * from 'my.component' // using my.service via DI
export * from 'my.service'

Тогда вы можете получить ошибку вроде EXCEPTION: Can't resolve all parameters for MyComponent: (?),

Чтобы это исправить, вы должны экспортировать сервис перед компонентом:

export * from 'my.service'
export * from 'my.component' // using my.service via DI
  1. Решение моей проблемы:

Такая же ошибка может произойти из-за circular dependency что вызывает undefined сервис импорт. Проверять, console.log(YourService) после его импорта (в вашем тестовом файле - где проблема возникает). Если он не определен, возможно, вы создали файл index.ts (баррель), экспортирующий как службу, так и файл, использующий его (компонент / эффект / все, что вы тестируете) - путем импорта службы из файла индекса, куда экспортируются оба (делая это полный круг).

Найдите этот файл и импортируйте нужный сервис напрямую из your.service.ts файл вместо индекса.

[JEST и ANGULAR]

Кроме того, проблема может возникнуть, если вы используете внешний модуль и не импортируете его, а используете в своей службе.

Пример:

import { TestBed } from '@angular/core/testing';
import <ALL needed> from '@ngx-translate/core';

import { SettingsService } from '../../../app/core/services/settings/settings.service';


describe('SettingsService', () => {
  let service: SettingsService;

  beforeAll(() => {
    TestBed.configureTestingModule({
      providers: [
        SettingsService,
        <All needed>
      ]
    });
    service = TestBed.inject<SettingsService>(SettingsService);
  });

  it('should be created', () => {
    expect(service).toBeTruthy();
  });

});

Ошибки ни к чему не приведут... Но если вы сделаете это так:

import { TestBed } from '@angular/core/testing';

import { TranslateModule } from '@ngx-translate/core';

import { SettingsService } from '../../../app/core/services/settings/settings.service';


describe('SettingsService', () => {
  let service: SettingsService;

  beforeAll(() => {
    TestBed.configureTestingModule({
      imports: [TranslateModule.forRoot()], <---
      providers: [
        SettingsService
      ]
    });
    service = TestBed.inject<SettingsService>(SettingsService);
  });

  it('should be created', () => {
    expect(service).toBeTruthy();
  });

});

Проблема исчезает.

[JEST и ANGULAR]

В моем случае основной причиной является циклическая зависимость, а не ситуация «импорт службы из индекса». А также ng build <project> --prod не нашел "круговой зависимости".

Решение:

В сервисе / компоненте инъекция Injector а также injector.get(Service) вместо.

[Jest и Angular] В моем случае я создавал фиктивный класс компонента, унаследовавший базовый компонент, который меня интересовал при тестировании. Проблема заключалась в том, что он был настроен на использование конструктора по умолчанию, поэтому TestBed не имел возможности внедрить для меня stubService. Вот как выглядит код:

      class DummyComponent extends MyBaseComponent {
  constructor(localizationService: LocalizationService) {
    super(localizationService) // this is needed constructor
  }
...
let fixture: ComponentFixture<DummyComponent>
beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [DummyComponent],
      imports: [{ provide: MyService, useValue: MyStubService}],
    })
  })
   fixture = TestBed.createComponent(DummyComponent) // <-- It was failing here
}

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

Другие вопросы по тегам