Тестирование - не удается разрешить все параметры для (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"
]
}
Проблема не была решена в выбранном ответе, который является просто рекомендацией для написания тестов, а скорее в комментариях, и вы должны перейти по ссылке и найти ее там. Так как у меня была другая проблема с той же ошибкой, я добавлю оба решения здесь.
- Решение проблемы ОП:
Если у вас есть бочка (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
- Решение моей проблемы:
Такая же ошибка может произойти из-за 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
}
Оглядываясь назад, это кажется более очевидным, потому что конкретный класс должен будет определить конструктор для получения услуги. Я просто подумал, что это будет конструктор по умолчанию.