如何用笑话和可选的弹珠测试复杂的 ngrx/rxjs 服务
How to test complex ngrx/rxjs service with jest and optionally with marbles
我是 javascript 测试的新手,我找不到明确的例子来学习如何测试这些类型的方法。我显然需要一些帮助!
这是我要测试的两种方法:
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);
}));
}
我按如下方式启动了我的 spec.ts :
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);
});
});
我尝试了很多方法,例如订阅或弹珠......但我无法让它工作。
我想检查结果 (observableFromApi) 并检查 post 请求:
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);
});
});
你能帮帮我吗?
编辑:
这样就开始工作了,但是我有一个错误:
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);
});
});
});
这是错误,我认为选择器没有正确模拟,没有调用 flatMap,我在选择器之前添加了一个 tap,它被调用了。
console.error
Error: Uncaught [TypeError: Cannot read property 'ids' of undefined]
编辑 2:
当我尝试模拟我的选择器时出现以下错误:
这就是我声明选择器的方式,请问这里有什么问题吗?
export const getFilterById = (id) => createSelector(selectAll, (allItems: Filter[]) => {
if (allItems) {
return allItems.find((item: Filter) => {
return item.id === id;
});
} else {
return null;
}
});
我通过提取方法中的选择器找到了解决方案:
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));
}
然后我在测试中模拟了这个方法:
searchService._getFilterById = jest.fn(() => of(filterForSelector));
我是 javascript 测试的新手,我找不到明确的例子来学习如何测试这些类型的方法。我显然需要一些帮助!
这是我要测试的两种方法:
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);
}));
}
我按如下方式启动了我的 spec.ts :
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);
});
});
我尝试了很多方法,例如订阅或弹珠......但我无法让它工作。 我想检查结果 (observableFromApi) 并检查 post 请求:
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);
});
});
你能帮帮我吗?
编辑:
这样就开始工作了,但是我有一个错误:
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);
});
});
});
这是错误,我认为选择器没有正确模拟,没有调用 flatMap,我在选择器之前添加了一个 tap,它被调用了。
console.error
Error: Uncaught [TypeError: Cannot read property 'ids' of undefined]
编辑 2:
当我尝试模拟我的选择器时出现以下错误:
这就是我声明选择器的方式,请问这里有什么问题吗?
export const getFilterById = (id) => createSelector(selectAll, (allItems: Filter[]) => {
if (allItems) {
return allItems.find((item: Filter) => {
return item.id === id;
});
} else {
return null;
}
});
我通过提取方法中的选择器找到了解决方案:
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));
}
然后我在测试中模拟了这个方法:
searchService._getFilterById = jest.fn(() => of(filterForSelector));