【问题标题】:Unit testing angular 2 Services does not run callback after injection单元测试角度2服务在注入后不运行回调
【发布时间】:2018-03-15 04:05:38
【问题描述】:

所以我已经为我的组件创建了单元测试,但我希望为我的个人服务单独创建一些测试。但是,当我尝试注入它们时(正在测试的服务方法不是异步的)。

describe('SearchService', () => {
    beforeEach(() => {
        TestBed.configureTestingModule({
            providers: [
                SearchService
            ]
        });

    });


    it("should build Url String", () => {
        inject([SearchService], (searchService: SearchService) => {
            spyOn(searchService, 'buildURL');
            console.log("should be logging like a boss");
            searchService.buildURL("test", "coursename", 2);
            expect(searchService.buildURL).toHaveBeenCalled();
            expect(searchService.buildURL("test", "coursename", 2)).toBe(['1']);
            expect(searchService.buildURL("test", "coursename", 2)).toBeFalsy();
        });
    });

});

Inject 从未真正运行回调!测试识别 it 语句,但没有错误地通过。

里面的 console.log 语句永远不会运行,并且设计为失败的测试通过了,所以我假设注入运行失败。

【问题讨论】:

  • 如果没有运行,应该有错误。如果您确定没有错误,请提供stackoverflow.com/help/mcve
  • @estus 没有错误。事实上,整个测试以优异的成绩通过。我可以在注入之外控制日志,但在 console.log 内部永远不会运行。
  • @estus,改进它并将其剥离到最低限度
  • 请提供一种方法来复制问题 - Plunker、Stackblitz 等。因为代码看起来没问题,除了你之外没有人能够找出问题所在。
  • 您确定问题不在于 1 个额外的 clojure 吗? it("should build Url String", () => { ?尝试删除它以获取此it("should build Url String", inject([SearchService], (searchService: SearchService) => {

标签: angular unit-testing karma-jasmine


【解决方案1】:

您只是嵌套了 1 个额外的 clojure,这就是它不起作用的原因。

it("should build Url String", () => {
        inject([SearchService], (searchService: SearchService) => {
            spyOn(searchService, 'buildURL');
            console.log("should be logging like a boss");
            searchService.buildURL("test", "coursename", 2);
            expect(searchService.buildURL).toHaveBeenCalled();
            expect(searchService.buildURL("test", "coursename", 2)).toBe(['1']);
            expect(searchService.buildURL("test", "coursename", 2)).toBeFalsy();
        });
    });

如下改变它以使其工作:

it("should build Url String", inject([SearchService], (searchService: SearchService) => {
            spyOn(searchService, 'buildURL');
            console.log("should be logging like a boss");
            searchService.buildURL("test", "coursename", 2);
            expect(searchService.buildURL).toHaveBeenCalled();
            expect(searchService.buildURL("test", "coursename", 2)).toBe(['1']);
            expect(searchService.buildURL("test", "coursename", 2)).toBeFalsy();
    })
);

原因是,由于您在另一个clojure 内执行inject,它将在另一个范围内执行它,it 的第二个参数应该是一个带有测试的函数,但是由于您传入了一个空的 clojure 它将简单地解析为true

下面是一个例子:

() => { // calling this clojure it will return null/undefined
  () => { // calling this clojure it will return '1'
    return '1';
  }
}

【讨论】:

    【解决方案2】:

    编辑:2 添加了如何使用 HTTP 调用存根数据替换原始示例对 Angular 2/4 服务进行单元测试的完整示例。单元测试服务 IMO 与官方和第三方指南有些不同的优秀示例。

    编辑:重新阅读官方指南,在上述 cmets 中的@AnteJablanAdamović 指出它应该是

    it('should tell ROUTER to navigate when hero clicked',
      inject([Router], (router: Router) => { // ...
    }));
    

    https://angular.io/guide/testing#the-inject-function

    我不确定您是否可以将其包装在 fakeasync(为什么不呢?)或 async 作为回调,但这是我最初问题的正确答案(没有人用 50+ 赏金和10+ 赞成票?!)。

    但是,下面的策略是一种更清洁/更快的方式来执行此 imo,而不是将注入粘贴到每个“it”语句中,方法是将其包含在 BeforeEach 中;

    很遗憾,Karma 或 Angular 没有抛出任何错误或警告标志。

    这是我提供的原始答案,但也可以作为替代方式:


    我使用 testBet.get 在 beforeEarch 中注入服务:比大多数指南建议的 IMO 好得多。

    如果您在测试服务时遇到问题,请尝试本指南:涵盖具有依赖关系的简单或复杂服务:

    http://www.kirjai.com/testing-angular-services-with-dependencies/

     describe('SearchService', () => {
    // IMPORTANT - declase variables we'll set in before each so every "it statement // can reach them
    
        let searchService: SearchService;
        let backend: MockBackend;
        let setupConnections;
    
    
            class MockActivatedRoute extends ActivatedRoute {
                constructor() {
                    super();
                    this.params = Observable.of({ 'searchterm': '*', 'sorttype': 'relevant', 'pagenumber': 1, 'facet': '' });
                }
            }
            const MockRouter = {
                navigate: (x) => ({
                    then: () => ({})
                })
            };
            beforeEach(() => {
                TestBed.configureTestingModule({
                    imports: [HttpModule],
                    providers: [
    // below required for HTTP substitution testing
                        MockBackend,
                        BaseRequestOptions,
                        {
                            provide: Http,
                            useFactory: (backend: MockBackend, options: BaseRequestOptions) => new Http(backend, options),
                            deps: [MockBackend, BaseRequestOptions]
                        },
                        AnalyticsService,
                        { provide: ActivatedRoute, useClass: MockActivatedRoute },
                        {
                            provide: Router,
                            useValue: MockRouter
                        },
                        SearchService
                    ]
                });
    
    // set our values in before each and use Testbed to inject services
            searchService = TestBed.get(SearchService);
            backend = TestBed.get(MockBackend);
    

    您可以使用 if 语句设置路径,例如上面的 setupConnections 指南链接,但除非您在调用中做一些不寻常的事情,否则您不需要路径匹配,所以这很好

     setupConnections = (backend: MockBackend, options: any) => {
                backend.connections.subscribe((connection: MockConnection) => {
                    const responseOptions = new ResponseOptions(options);
                    const response = new Response(responseOptions);
                    connection.mockRespond(response);
                });
            };
    
            });
    

    注意 async 不是 fakeAsync!!!!通常我在组件单元测试中使用 fakeAsync 很好,但是在以这种方式对这些服务进行单元测试时,我遇到了一些错误,YMMV

        it('should get suggestions for search drop down and match of mock results for test', async(() => {
            console.log('running get Suggestions');
    // here we set out HTTP data response stub: put return data in body
            setupConnections(backend, {
                body: {
                    suggestions:
                    ["6-minute walk test",
                },
                status: 200
            });
    // resolve HTTP call with subscribe and do test in call back.
            searchService.getSuggestions('test').subscribe((x) => {
                console.log(x);
                expect(x).toEqual(['6-minute walk test']);
            });
    
        });
    

    【讨论】:

      猜你喜欢
      • 2017-06-13
      • 2019-03-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-07-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多