[英]Unit testing angular 2 Services does not run callback after injection
So I've created unit tests for my components but want some for my individual services in isolation. 因此,我已经为组件创建了单元测试,但希望单独为我的单个服务提供一些单元测试。 However when i try to inject them(the service method being tested is not async).
但是,当我尝试注入它们时(正在测试的服务方法不是异步的)。
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 never actually runs callback! 注入实际上从未运行过回调! test recognizes it statement but passes without error.
测试识别出它的语句,但没有错误通过。
the console.log statement inside never runs and tests designed to fail pass, so I assume inject is failing to run. 其中的console.log语句永远不会运行,并且测试旨在通过无法通过,因此我认为注入无法运行。
EDIT: 2 added complete example of how to Unit test Angular 2/4 service with HTTP call stub data replacing original example. 编辑:2添加了如何使用HTTP调用存根数据替换原始示例来对Angular 2/4服务进行单元测试的完整示例。 Excellent example of unit testing a service IMO somewhat different than official and third party guide.
对服务IMO进行单元测试的出色示例,与官方指南和第三方指南有些不同。
EDIT: re-read official guide and after @AnteJablanAdamović in the above comments pointed out it's supposed to be 编辑:重新阅读官方指南,并在上面评论中的@AnteJablanAdamović指出之后,应该是
it('should tell ROUTER to navigate when hero clicked',
inject([Router], (router: Router) => { // ...
}));
https://angular.io/guide/testing#the-inject-function https://angular.io/guide/testing#the-inject-function
I'm not sure if you can wrap it in an fakeasync(why not?) or async as a call back but this is the correct answer to my original question(how did nobody figure this out with a 50+ bounty and 10+ upvotes?!). 我不确定您是否可以将其包装在fakeasync(为什么不这样做)或async中作为回叫,但这是对我原始问题的正确答案(没有人能以50+的赏金和10+的投票数解决此问题?!)。
However strategy below is a cleaner/faster way to do this imo instead of pasting inject into every "it" statement by including it in BeforeEach; 但是,下面的策略是一种执行此imo的更简洁/快捷的方法,而不是通过在BeforeEach中包括它来将注入粘贴到每个“ it”语句中;
It's a shame Karma or angular doesn't throw any error or warning flags. 这是一个耻辱,因果报应或angular不会抛出任何错误或警告标志。
Here's the original answer I provided but also works as an alternative way: 这是我提供的原始答案,但也可以用作替代方法:
I used testBet.get to inject service in beforeEarch: Much better than what most guides suggest IMO. 我使用testBet.get在beforeEarch中注入服务:比大多数指南建议的IMO好得多。
Try this guide if your having issues testing services: covers simple or complex services with dependencies: 如果您在测试服务方面遇到问题,请尝试使用本指南:涵盖具有依赖性的简单或复杂服务:
http://www.kirjai.com/testing-angular-services-with-dependencies/ 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);
you can set path with if statements like in guide link above for setupConnections but unless your doing something unusual with a call, you don't need to have path match so this is fine 您可以使用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);
});
};
});
Note the async not fakeAsync!!!! 注意异步不是fakeAsync !!!! Usually I use fakeAsync fine in component unit testing but I got some errors doing this when unit testing these services this way, YMMV
通常我在组件单元测试中使用fakeAsync很好,但是当以这种方式对这些服务进行单元测试时,这样做会出现一些错误
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']);
});
});
You simply nested 1 extra clojure
and that's why it won't work. 您只嵌套了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();
});
});
Change it like below to make it work: 如下更改它以使其工作:
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();
})
);
The reason is, since you perform inject
within another clojure
it will perform it within another scope, the 2nd parameter of it
should be a function with tests but since you passed in an empty clojure
it will simply resolve to true
. 原因是,因为你执行
inject
另一种内clojure
将在另一范围内履行它的第二个参数it
应该与测试功能,但因为你在一个空传递clojure
它只会决心true
。
Here's an example of what's going on: 这是发生了什么的示例:
() => { // calling this clojure it will return null/undefined
() => { // calling this clojure it will return '1'
return '1';
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.