【问题标题】:Angular-Testing: How to provide configuration object to service class with TestBed?Angular-Testing:如何使用 TestBed 为服务类提供配置对象?
【发布时间】:2019-03-08 17:12:53
【问题描述】:

我有一个需要将配置对象传递给服务的 Angular 服务:

// my.module.ts
@NgModule({ ... })
export class MyModule {
    static forRoot(config: MyServiceConfig): ModuleWithProviders {
        return {
            ngModule: MyModule,
            providers: [{ provide: MyServiceConfig, useValue: config }],
        };
    }
}


//my.service.ts
export class MyService {
        constructor(private _http: HttpClient, @Optional() config: MyServiceConfig) {
        if (config) {
            if (!config.attr1) {
                throw new Error('You must provide the attr1 to use this Module.');
            } else if (!config.attr2) {
                throw new Error('You must provide the attr2 to use this Module.');
            } else {
                this.attr1 = config.attr1;
                this.attr2 = config.attr2;
            }
        } else {
            throw new Error(
                'You must provide a MyServiceConfig object with the attr1 and the attr2 to use this module.',
            );
        }
    }

}

这一切都有效,但我想围绕向服务提供该配置对象编写几个测试。我在测试文件中有以下beforeEach,当未提供配置对象时它按预期抛出错误:

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

但是当我试图将它从beforeEach 移到单独的测试中时,我无法正确抛出错误。如果它完全按照上面的方式调用,但在测试中,它会:

it('should do something', () => {
    TestBed.configureTestingModule({
        imports: [HttpClientTestingModule],
        providers: [FeedbackService],
    });
});

我在try/catch 块中尝试了上述操作,试图捕捉错误,但它给了我一个误报。我尝试了expect(() => {}).toThrowError()toThrow() 方法,但即使将TestBed.configureTestingModule() 放在expect 中的箭头函数内也不起作用。这样做不会引发错误。

有没有办法做到这一点?另外,有没有办法向服务提供配置对象以测试它是否将服务属性设置为正确的值?

【问题讨论】:

    标签: angular unit-testing testing jasmine


    【解决方案1】:

    只需为配置对象提供一个值:

    describe("FeedbackService", ()=>{
        beforeEach(() => {
            TestBed.configureTestingModule({
                imports: [HttpClientTestingModule],
                providers: [FeedbackService]
            });
        });
    
        describe("when config object is provided", ()=>{
            let dummyConfig : Object;
            beforeEach(()=>{
              dummyConfig = {/* set some properties*/};
              TestBed.overrideProvider(MyServiceConfig, {useValue: dummyConfig});
            });
    
            it("should not explode", ()=>{
              // a test in which the config object is dummyConfig
            });
        });
    });
    

    旁注:我看不出用@Optional 装饰配置对象的意义,并在没有提供令牌值时抛出。您基本上是在重新实现默认未提供的逻辑。

    【讨论】:

    • 我尝试了您的解决方案,但仍然遇到相同的错误。你对 @Optional 装饰器有一个很好的观点。我读过的所有教程都有这个,我不确定如何要求它,但删除装饰器似乎给了我同样的错误,所以我确实删除了。
    • 以下工作:TestBed.overrideProvider(FeedbackService, { useFactory: () => new FeedbackService(null, config) }); 我为 HttpClient 传入了 null,因为我没有测试 HTTP 调用。
    • 你是如何用 Injectable 装饰那个服务的?介意添加那部分代码吗?我不喜欢你现在的做法。
    【解决方案2】:

    我使用了@Jota.Toledo 的一些答案并进行了编辑以获得以下测试文件:

    import { TestBed } from '@angular/core/testing';
    
    import { MyService } from './my.service';
    import { MyServiceConfig } from './my-service-config';
    
    describe('MyService', () => {
        beforeEach(() => {
            TestBed.configureTestingModule({
                imports: [HttpClientTestingModule],
                providers: [MyService],
            });
        });
    
        describe('config object provided', () => {
            let config: MyServiceConfig;
            const attr1 = 'https://my-test-api.test.com';
            const attr2 = 'testing';
    
            beforeEach(() => {
                config = null;
            });
    
            it('should use the values passed in the config for the attr1 and attr2', () => {
                config = { attr1, attr2 };
                TestBed.overrideProvider(MyService, { useFactory: () => new MyService(null, config) });
                const service: MyService = TestBed.get(MyService);
    
                expect(service.attr1).toBe(attr1);
                expect(service.attr2).toBe(attr2);
            });
    
            it('should throw an error if config object is provided but not the attr1 attribute', () => {
                try {
                    config = { attr1: null, attr2 };
                    TestBed.overrideProvider(MyService, { useFactory: () => new MyService(null, config) });
                    const service: MyService = TestBed.get(MyService);
                } catch (e) {
                    expect(e.message).toBe('You must provide the api URL to use this module.');
                }
            });
    
            it('should throw an error if config object is provided but not the attr2 attribute', () => {
                try {
                    config = { attr1, attr2: null };
                    TestBed.overrideProvider(MyService, { useFactory: () => new MyService(null, config) });
                    const service: MyService = TestBed.get(MyService);
                } catch (e) {
                    expect(e.message).toBe('You must provide the feedback source to use this module.');
                }
            });
        });
    
        describe('config object not provided', () => {
            beforeEach(() => {
                TestBed.overrideProvider(MyService, { useFactory: () => new MyService(null, null) });
            });
            it('should throw an error if no config object provided', () => {
                try {
                    const service: MyService = TestBed.get(MyService);
                } catch (e) {
                    expect(e.message).toBe(
                        'You must provide a MyServiceConfig object with the attr1 and the attr2 to use this module.',
                    );
                }
            });
        });
    });
    

    这在应该发生的时候正确地抛出了错误,并且我能够检查 message 属性以确保它在正确的时间抛出了正确的错误。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-11-21
      • 2017-05-19
      • 2013-07-28
      • 1970-01-01
      • 2019-10-24
      • 1970-01-01
      • 1970-01-01
      • 2013-06-23
      相关资源
      最近更新 更多