首页 > 解决方案 > Angular Unit Testing - 使用 TestBed 模拟注入服务的异步调用

问题描述

我需要为以下内容编写单元测试DataService

@Injectable()
export class DataService {
   constructor(private config: ConfigService, private http: HttpClient) {

   }

   .....

   someMethod(){
        let apiUrl = this.config.get('api').url;   // LINE 1
   }
}

ConfigService被注入的有DataService一个load从 json 文件获取配置的函数。该load函数将在应用程序初始化时调用。

export function configServiceFactory(config: ConfigService) {
    return () => config.load();
}

...

providers: [
        ConfigService,
        {
            provide: APP_INITIALIZER,
            useFactory: configServiceFactory,
            deps: [ConfigService],
            multi: true
        }
    ]

这是data-service.spect.ts文件的一部分,

...

beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [
        HttpClientTestingModule
      ],
      providers: [DataService, ConfigService]
    });

    mock = TestBed.get(HttpTestingController);
    service = TestBed.get(DataService);
  });

....

所以当我运行测试时,LINE 1我知道这this.config.get('api')是未定义的。我可以理解这是因为ConfigService没有从 JSON 加载数据。那么现在我怎样才能让注入的服务在单元测试期间也进行异步调用呢?

标签: angularkarma-jasmineangular-unit-testangular-test

解决方案


在编写单元测试时,您可能希望模拟您拥有的每个依赖项。您已经HttpClient通过导入做到HttpClientTestingModule了这一点,因此您需要对ConfigService.

有两种方法可以做到这一点。

1-假服务(存根)

export class ConfigServiceStub {
    get(input: string) {
        // return static value instead of calling an API
    }
}

...

beforeEach(() => {
    TestBed.configureTestingModule({
        imports: [
            HttpClientTestingModule
        ],
        providers: [DataService, 
                   {provide: ConfigService, useClass: ConfigServiceStub}
        ]
    });

    mock = TestBed.get(HttpTestingController);
    service = TestBed.get(DataService);
});

通过这种方式,您DataService不会调用 real ConfigService,而是调用 的get方法ConfigServiceStub。使用Stubs 时,无需担心其他依赖ConfigService有。您只需实现要覆盖的方法。

2-间谍

您可以在不想调用的方法上创建间谍。

it('run some test', inject([DataService], (service: DataService) => {
    spyOn(service.config, 'get').and.returnValue('someString');

    // run your tests here
});

即使ConfigService.get在上面的示例中方法不会被调用,Angular 仍然需要创建一个实例,ConfigService在某些示例中可能很难这样做,或者可能导致创建太多其他服务以进行简单测试。

我会选择选项1

有关间谍的更多信息,请查看此处


推荐阅读