I'm new to javascript testing, and I can't find clear examples to learn how to test these kinds of methods. I clearly need some help here please!
Here are the two methods I'm trying to test :
public _getSearch(search: Search): Observable < any[] > {
const timeOptions: DateCriteria = this._getTimeStartEnd(search);
const searchField: string = this._decodeHexIfNecessary(search.searchField.trim());
return this.store.pipe(
select(getFilterById(search.filterId)),
flatMap((filter: Filter) => {
const indicators = [{
'criterias': this.filterService.getActiveCriterias(filter)
}
];
search.lastSearch = new TransactionRequest(
search.offset, search.pageSize, timeOptions.begin, timeOptions.end,
indicators, search.sortData, searchField);
const request = JSON.stringify(search.lastSearch);
return this.rest.post(search.urls.get, request, true);
}));
}
public _countTotalSearch(search: Search): Observable < number > {
const timeOptions: DateCriteria = this._getTimeStartEnd(search);
return this.store.pipe(
select(getFilterById(search.filterId)),
first(),
concatMap((filter: Filter) => {
const indicators = [{
'criterias': this.filterService.getActiveCriterias(filter)
}
];
const request = new TransactionRequest(
0, 0, timeOptions.begin, timeOptions.end,
indicators, null, search.searchField.trim());
return this.rest.post(search.urls.count, JSON.stringify(request), true);
}));
}
I initiated my spec.ts as follows :
describe('SearchService', () => {
let searchService: SearchService;
let store: MockStore;
let itemsRestService: ItemsRestService;
let restService: RestService;
let scheduler: TestScheduler;
let filterService: FilterService;
const filterForSelector = TestModelFactory.generateFilter();
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
TranslateModule.forRoot(),
HttpClientTestingModule
],
providers: [
SearchService,
RestService,
ItemsRestService,
ReturnCodeRestService,
{
provide: FilterService,
useValue: {}
},
{
provide: DataMapperService,
useValue: {}
},
provideMockStore({
selectors: [
{
selector: 'getFilterById',
value: filterForSelector
}
]
})
]
});
searchService = TestBed.inject(SearchService);
itemsRestService = TestBed.inject(ItemsRestService);
restService = TestBed.inject(RestService);
filterService = TestBed.inject(FilterService);
scheduler = new TestScheduler(((actual, expected) => {
expect(actual).toEqual(expected);
}));
store = TestBed.inject(MockStore);
});
});
I tried a lot of things, like subscribing, or marbles... But I can't make it work. I'd like to check the result (observableFromApi) and to check the post request as well :
describe('getSearch', () => {
it('should select filters in store and call api', done => {
// init vars
const dateCriteria = new DateCriteria(faker.date.recent().getTime(), faker.date.recent().getTime());
const resultFromApi = faker.random.word() as any;
const observableFromApi = of(resultFromApi);
const search = TestModelFactory.generateSearch();
// spy
spyOn(restService, 'post');
spyOn(searchService, '_getTimeStartEnd');
// mocks
filterService.getActiveCriterias = jest.fn(() => faker.random.word() as any);
searchService._getTimeStartEnd = jest.fn(() => dateCriteria);
restService.post = jest.fn(() => observableFromApi);
// call and expect
expect(searchService._getSearch(search)).toEqual(observableFromApi);
expect(restService.post).toHaveBeenCalledWith(search.urls.get, '', true);
});
});
Can you help me please?
EDIT :
This way it starts to work, but I have an error :
describe('getSearch', () => {
it('should select filters in store and call api', done => {
// init vars
const dateCriteria = new DateCriteria(faker.date.recent().getTime(), faker.date.recent().getTime());
const resultFromApi = faker.random.word() as any;
const observableFromApi = of(resultFromApi);
const search = TestModelFactory.generateSearch();
// spy
spyOn(restService, 'post');
spyOn(searchService, '_getTimeStartEnd');
// mocks
filterService.getActiveCriterias = jest.fn(() => faker.random.word() as any);
searchService._getTimeStartEnd = jest.fn(() => dateCriteria);
// const expectedObservable = cold('a|', {a : observableFromApi});
// restService.post = jest.fn(() => expectedObservable);
restService.post = jest.fn(() => observableFromApi);
searchService._getSearch(search).subscribe(res => {
done();
expect(res).toBe(observableFromApi);
expect(restService.post).toHaveBeenCalledWith(search.urls.get, '', true);
});
});
});
Here is the error, I think the selector is not mocked properly, the flatMap is not called, I added a tap before the selector and it was called.
console.error
Error: Uncaught [TypeError: Cannot read property 'ids' of undefined]
EDIT 2 :
I have the following error when I try to mock my selector :
This is how I declared the selector, what is wrong here please ?
export const getFilterById = (id) => createSelector(selectAll, (allItems: Filter[]) => {
if (allItems) {
return allItems.find((item: Filter) => {
return item.id === id;
});
} else {
return null;
}
});
I found a solution by extracting the selector in a method :
public _getSearch(search: Search): Observable<any[]> {
const timeOptions: DateCriteria = this._getTimeStartEnd(search);
const searchField: string = this._decodeHexIfNecessary(search.searchField.trim());
const filterObservable: Observable<Filter> = this._getFilterById(search);
const result = filterObservable.map((filter: Filter) => {
// Préparation de la requête
const indicators = [{ 'criterias': this.filterService.getActiveCriterias(filter) }];
search.lastSearch = new TransactionRequest(
search.offset, search.pageSize, timeOptions.begin, timeOptions.end,
indicators, search.sortData, searchField
);
const request = JSON.stringify(search.lastSearch);
return this.rest.post(search.urls.get, request, true);
});
return result as Observable<any>;
}
public _getFilterById(search: Search) {
return this.store.select(getFilterById(search.filterId));
}
And then I mocked this method in my tests :
searchService._getFilterById = jest.fn(() => of(filterForSelector));