Тестирование модуля angular 2 Сервисы не запускают обратный вызов после инъекции
Итак, я создал модульные тесты для своих компонентов, но хочу, чтобы некоторые из моих отдельных служб были изолированы. Однако, когда я пытаюсь их внедрить (тестируемый метод обслуживания не является асинхронным).
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 никогда не запускает обратный вызов! test распознает его, но передает без ошибок.
оператор console.log внутри никогда не запускается, и тесты, предназначенные для отказа, проходят, поэтому я предполагаю, что инъекция не запускается.
Ответы
Ответ 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();
})
);
Причина в том, что, поскольку вы выполняете inject
внутри другого clojure
, он будет выполнять его в другой области, второй параметр it
должен быть функцией с тестами, но поскольку вы передали пустой clojure
просто разрешит true
.
Вот пример того, что происходит:
() => { // calling this clojure it will return null/undefined
() => { // calling this clojure it will return '1'
return '1';
}
}
Ответ 2
EDIT: 2 добавлен полный пример того, как Unit test Angular услуга 2/4 с данными HTTP-заглушки, заменяющими исходный пример. Отличный пример модульного тестирования службы IMO несколько отличается от официального и стороннего руководства.
EDIT: перечитайте официальное руководство и после @AnteJablanAdamović в приведенных выше комментариях указали, что он должен быть
it('should tell ROUTER to navigate when hero clicked',
inject([Router], (router: Router) => { // ...
}));
https://angular.io/guide/testing#the-inject-function
Я не уверен, если вы можете обернуть его в fakeasync (почему бы и нет?) или асинхронно, как обратный вызов, но это правильный ответ на мой оригинальный вопрос (как никто не оценил это с 50+ щедростью и 10+ upvotes?!).
Однако стратегия ниже - это более чистый/быстрый способ сделать это imo, а не вставлять инъекцию в каждое выражение "it", включив его в BeforeEach;
Это позорная карма или 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 not 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']);
});
});