测试 - 无法解析(ClassName)

时间:2016-09-13 14:20:18

标签: angular karma-jasmine angular2-services angular2-testing angular2-injection

上下文

我创建了一个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.tsApiConnectorService的规范文件),ApiService的测试将会成功。
  • 如果我从已加载的测试中删除api-service.spec.tsApiService的规范文件),ApiConnectorService的测试将会成功。

为什么会出现此错误?看起来我的两个文件之间的上下文存在冲突,我不知道为什么以及如何解决这个问题。

6 个答案:

答案 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)

这个问题在选择的答案中并没有真正解决,这实际上只是编写测试的建议,而是在评论中,你必须按照链接并在那里搜索它。由于我遇到了同样错误的另一个问题,我将在这里添加两个解决方案。

  1. 解决OP的问题:
  2. 如果您有这样的桶(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. 解决我的问题:
    2. 由于导致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 没有找到“循环依赖”。

解决方案:

在服务/组件中,改为注入 Injectorinjector.get(Service)