我创建了一个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
添加我的第二个spec文件,该文件适用于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
的测试将会成功。为什么会出现此错误?看起来我的两个文件之间的上下文存在冲突,我不知道为什么以及如何解决这个问题。
答案 0 :(得分:16)
这是因为在测试环境中无法从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');
});
})));
});
答案 1 :(得分:9)
万一有人来了,而您正在使用Jest测试您的Angular应用程序(希望我们正在成为少数群体),如果不发出装饰器,则会遇到此错误。您需要更新tsconfig.spec.json
文件,使其看起来像:
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"emitDecoratorMetadata": true,
"outDir": "../../out-tsc/spec",
"types": [
"jest",
"node"
]
},
"files": [
],
"include": [
"**/*.spec.ts",
"**/*.d.ts"
]
}
答案 2 :(得分:6)
这个问题在选择的答案中并没有真正解决,这实际上只是编写测试的建议,而是在评论中,你必须按照链接并在那里搜索它。由于我遇到了同样错误的另一个问题,我将在这里添加两个解决方案。
如果您有这样的桶(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
文件而不是索引中导入所需的服务。
答案 3 :(得分:0)
[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();
});
});
问题消失了。
答案 4 :(得分:0)
[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
}
回想起来似乎更明显,因为具体的类必须定义构造函数才能获取服务。我只是认为那将是默认构造函数。
答案 5 :(得分:0)
[JEST 和 ANGULAR]
就我而言,根本原因是循环依赖,而不是“从索引导入服务”的情况。并且 ng build <project> --prod
没有找到“循环依赖”。
解决方案:
在服务/组件中,改为注入 Injector
和 injector.get(Service)
。