【问题标题】:Testing - Can't resolve all parameters for (ClassName)测试 - 无法解析 (ClassName) 的所有参数
【发布时间】:2017-01-21 04:37:44
【问题描述】:

上下文

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

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

【问题讨论】:

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


    【解决方案1】:

    使用 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"
      ]
    }
    

    【讨论】:

    • 谢谢。错误消息中没有任何内容可以帮助解决这个问题。关键是“emitDecoratorMetadata”:true
    【解决方案2】:

    这是因为在测试环境中,Http 服务无法从HttpModule 解析。它依赖于平台浏览器。在测试期间,您甚至不应该尝试进行 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');
    	});
      })));
    });

    【讨论】:

    • 为什么不直接使用MockBackend, BaseRequestOptions 而不是{ provide: MockBackend, useClass: MockBackend }, { provide: BaseRequestOptions, useClass: BaseRequestOptions },HttpModule 不是已经提供了这两个(我很确定它们是)。也可以将providers: [ { provide: Http, useFactory: (backend, options) => { return new Http(backend, options); }, deps: [MockBackend, BaseRequestOptions] }, 缩短为{provide: XHRBackend: useExisting: MockBackend}
    • 所以providers: [{provide: XHRBackend: useExisting: MockBackend}, SomeService] 应该给你同样的结果。
    • @GünterZöchbauer 是的,你是对的。我以前使用XHRBackend 作为令牌,但我忘记了我正在注入MockBackend 类型。所以我把它改成了MockBackend令牌。当时并没有考虑风格,只是在我测试时快速修复。
    • 据我所知,Angular.io 的文档也显示了这一点。你的回答很好。只是想指出来。我见过很多例子,人们使用过于复杂的配置,然后抱怨 DI 很麻烦 ;-)
    • 那么为什么我得到 ApiConnectorService 参数未解析?为什么只有当我开始两个测试时?
    【解决方案3】:

    在选择的答案中并没有真正解决问题,这实际上只是对编写测试的建议,而是在 cmets 中,您必须点击链接并在那里搜索。由于我遇到了同样错误的另一个问题,因此我将在此处添加两种解决方案。

    1. OP 问题的解决方案:

    如果你有这样的桶(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. 我的问题的解决方案:

    circular dependency 会导致undefined 服务导入,也会发生同样的错误。要检查,console.log(YourService) 导入后(在您的测试文件中 - 问题发生的地方)。如果未定义,您可能已经制作了一个 index.ts 文件(桶),导出服务和使用它的文件(组件/效果/您正在测试的任何内容) - 通过从导出两者的索引文件导入服务(使它完整的循环)。

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

    【讨论】:

      【解决方案4】:

      [JEST 和 ANGULAR]

      就我而言,根本原因是循环依赖,而不是“从索引导入服务”的情况。而ng build <project> --prod 没有找到“循环依赖”。

      解决方案:

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

      【讨论】:

        【解决方案5】:

        [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();
          });
        
        });
        

        问题消失了。

        【讨论】:

          【解决方案6】:

          [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
          }
          

          回想起来似乎更明显,因为具体类必须定义构造函数才能获得服务。我只是认为这将是默认构造函数。

          【讨论】:

            猜你喜欢
            • 2019-03-16
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2018-01-07
            • 2021-04-17
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多