单元测试角度2服务注入后不运行回调

时间:2017-10-03 21:04:17

标签: angular unit-testing karma-jasmine

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

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

});

注入从未实际运行回调! test识别它的声明但是没有错误地传递。

内部的console.log语句从不运行,并且测试设计为失败传递,因此我假设注入无法运行。

2 个答案:

答案 0 :(得分:3)

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

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

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

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

我不确定你是否可以把它包装在假的同步中(为什么不呢?)或async作为回调,但这是我原来问题的正确答案(没有人用50+的赏金来解决这个问题10+ upvotes?!)。

然而,下面的策略是一种更清洁/更快速的方式来实现这个imo,而不是通过将其包含在BeforeEach中来注入每个“it”语句;

耻辱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语句中的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']);
        });

    });

答案 1 :(得分:2)

你只是嵌套了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();
    })
);

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

这是一个正在发生的事情的例子:

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