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

Контекст

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

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

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

Проблема

Когда я тестирую ApiService, я инициализирую в beforeEach:

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

и он отлично работает.

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

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

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

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

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

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

Ответ 1

Это потому, что служба 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');
	});
  })));
});

Ответ 2

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

  • Решение проблемы OP:

Если у вас есть ствол (index.ts или multi export file), например:

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) после импорта (в вашем тестовом файле - там, где проблема возникает). Если он undefined, вы можете сделать файл index.ts(barrel), экспортирующий как сервис, так и файл, используя его (компонент/эффект/все, что вы тестируете) - путем импорта службы из индексного файла, где оба экспортируется (делает его полным).

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