测试 - 无法解析 (类名) 的所有参数



背景信息

我创建了一个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
        ]
    });
}));

,它工作得很好

然后我添加第二个规范文件,这是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的测试将成功。

为什么会出现这个错误?这似乎是我的两个文件之间的上下文冲突,我不知道为什么和如何解决这个问题。

使用Jest?

如果有人到达这里,你正在使用Jest来测试你的Angular应用程序(希望我们是越来越多的少数),你会遇到这个错误,如果你没有发出装饰器("emitDecoratorMetadata":true)。您需要更新您的tsconfig.spec.json文件,使其看起来像:

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

这是因为在测试环境中不能从HttpModule中解析Http服务。它依赖于平台浏览器。在测试期间,您甚至不应该尝试进行XHR调用。

因为这个原因,Angular提供了一个MockBackendHttp服务使用。我们在测试中使用这个模拟后端来订阅连接,并且可以在建立每个连接时模拟响应。

这里有一个简短的完整的例子,你可以使用

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. OP问题的解决方案:

如果你有一个桶(索引)。

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)导入后(在您的测试文件-问题发生的地方)。如果未定义,则可能已经创建了索引。Ts文件(桶)导出服务和使用它的文件(组件/效果/任何你正在测试的)-通过从导出服务的索引文件中导入服务(使其完整)。

    找到该文件并直接从your.service.ts文件而不是索引中导入所需的服务

    (开玩笑和角)

    在我的例子中,根本原因是循环依赖,而不是"import service from index"。的情况。ng build <project> --prod没有发现"循环依赖"。

    解决方案:

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

    (开玩笑和角)

    同样,当你使用一个外部模块,但你没有导入它,而是在你的服务中使用它时,可能会出现这个问题。

    ,

    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]在我的例子中,我创建了一个虚拟组件类,它继承了一个基组件,这是我感兴趣的测试。问题是它被设置为使用默认构造函数,所以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
    }
    

    回头看似乎更明显,因为具体类必须定义构造函数才能获得服务。

    相关内容

    • 没有找到相关文章

    最新更新