【问题标题】:Angular2 / Jasmine: Testing Service with another Service as a dependencyAngular2 / Jasmine:使用另一个服务作为依赖项测试服务
【发布时间】:2017-09-12 10:18:11
【问题描述】:

设置:加载配置数据的 APP_INITIALIZER 服务ConfigurationService 正在跨应用组件和服务使用。在对组件进行单元测试时,它会读取配置文件并正确评估。对使用配置的服务进行单元测试时,它会失败。

设置如下:

app.module.ts

export function ConfigurationFactory( config: ConfigurationService ): any {
  // this line actually triggers the loading of the config file  
  return () => config.load(); 
}

@NgModule({
  bootrstrap: {},
  ...,
  providers : [
    ConfigurationService,
    {
      deps: [ ConfigurationService ],
      multi: true,
      provide: APP_INITIALIZER,
      useFactory: ConfigurationFactory,   
    }
  ]
})

ConfigurationService.ts

@Injectable()
export class ConfigurationService {

  // error holder
  public error: boolean = false;
  // data holder
  public data: any = {};
  // config file url
  private configUrl: string = './assets/config/config.json';

  constructor( private http: Http ) {
  }
  /**
   * reads config file
   */
  public load(): Promise<any> {
    return this.http.get( this.configUrl )
      .map( ( response: any ) => response.json() )
      .toPromise()
      .then( ( data: any ) => this.data = data )
      .catch( ( err: any ) => Promise.resolve() );
  }

}

这里也是使用配置服务的TestableService.service.ts

@Injectable()
export class TestableService extends SomeAbstractHttpService {
  constructor( http: Http,
               private config: ConfigurationService ) {
    super( http );
  }

  public setRequest( sessionId: string, isIdent: boolean ): void {
    if ( isIdent ) {
      this.resourceUrl = `${ this.config.data.contextRootRest}/documents/ident/${sessionId}`;
    } else {
      this.resourceUrl = `${ this.config.data.contextRootRest}/documents/noident/${sessionId}`;
    }
    this.headers = new Headers();
    this.headers.append( 'Content-Type', 'application/json' );
    this.headers.append( 'Accept', 'application/json, application/pdf' );
    this.requestOptions = new RequestOptions( {
      headers: this.headers,
      responseType: ResponseContentType.Blob
    } );
  }
}

现在是规范文件,TestableService.service.spec.ts

export function ConfigurationFactory( config: ConfigurationService ): any {
  return () => config.load();
}

describe( 'TestableService', () => {

  // not ideal to hardcode it here, I would prefer using the same config file, which is used across the app!
  let mockConfig: any = {contextRootRest: 'rest/'};
  let theService: TestableService; 

  beforeEach( async(() => {
    TestBed.configureTestingModule( {
      imports: [
        HttpModule,
      ],
      providers: [
        TestableService,
        MockDataService,
        { provide: XHRBackend, useClass: MockBackend },
        ConfigurationService,
        {
          // Provider for APP_INITIALIZER
          deps: [ ConfigurationService ],
          multi: true,
          provide: APP_INITIALIZER,
          useFactory: ConfigurationFactory,
        },
      ]
    } );
  } ) );

  // I suspect, that using APP_INITIALIZER at this stage is goofy, 
  // as I do not in fact instantiate the app, only the service. 
  // What would be the correct way to do it here?

  beforeEach( inject( [ TestableService, XHRBackend ], ( service: TestableService, backend: MockBackend ) => {
    theService = service;
    mockBackend = backend;
  } ) );  

  it( 'should set request correctly', ( ) => {
    theService.setRequest( '1234567890', true );
    expect( theService.resourceUrl )
        .toEqual( `${ mockConfig.contextRootRest }/documents/ident/1234567890` );
  });
});

它以Expected 'undefined/documents/ident/1234567890' to equal 'rest/documents/ident/1234567890' 失败 - 这意味着配置服务根本没有在 TestableService 中加载。有什么想法吗?

【问题讨论】:

    标签: angular unit-testing dependency-injection jasmine


    【解决方案1】:

    上面的代码实际上很糟糕。几个小时后,我们找到了方法,这是一个更清洁、更好的解决方案。

    所以,由于配置文件在我们的资产目录中,我们实际上可以将它直接导入到规范文件中。我们还为 ConfigurationService 使用了一个模拟提供程序,仅此而已。这是 spec.ts 文件

    import { ConfigurationService } from '../../common/config/configuration.service';
    import * as config from '../../../assets/config/config.json';
    
    const mockConfig: any = config;
    
    export class ConfigurationFactory {
      // no methods as in the original ConfigurationService, just the object var
      public data: any = mockConfig;
    }
    
    describe( 'TestableService', () => {
    
      let theService: TestableService;
    
      beforeEach( () => {
        TestBed.configureTestingModule( {
          providers: [
            TestableService, ...
            { provide: ConfigurationService, useClass: ConfigurationFactory },
          ]
        } );
      } );
    
      beforeEach( inject( [ TestableService ],
          ( service: TestableService ) => {
            theService = service;
      } ) );
    
      it( 'should set request correctly', ( ) => {
        theService.setRequest( '1234567890', true );
        expect( theService.resourceUrl )
            .toEqual( `${ mockConfig.contextRootRest }/documents/ident/1234567890` );
      });
    }
    

    另一个以过于复杂的方式解决问题的例子,而表面上更简单的解决方案......:/

    【讨论】:

      猜你喜欢
      • 2016-04-16
      • 1970-01-01
      • 2017-09-17
      • 2017-10-31
      • 2013-06-11
      • 2014-09-04
      • 1970-01-01
      • 2018-12-23
      • 2021-09-11
      相关资源
      最近更新 更多