Angular - 使用 Pairwise 运算符测试 Observable
Angular - testing Observable with Pairwise operator
我正在使用 Angular 并且一直在使用 jasmine-marbles 编写单元测试。这是一个我似乎无法弄清楚的场景。我将如何测试 determineNextSteps$
?除了使用 jasmine-marbles 之外,我也愿意接受其他测试解决方案。
示例:
export class Service {
private person$: Observable<any> = this.store.pipe(
select(fromStore.selectPersonState),
map(({ person }) => person)
);
private parentSetsState$: Observable<any> = this.store.pipe(
select(fromStore.selectParentState)
);
private childSet$: Observable<any> = this.parentSetsState$.pipe(
map(({ parentSets }) =>
parentSets.find(set => set.name === 'FooBar')
)
);
determineNextSteps$: Observable<any> = this.childSet$.pipe(
tap(set =>
this.store.dispatch(
fromStore.determineStep({
set: set
})
)
),
pairwise(),
concatMap(pairedSet =>
this.person$.pipe(
first(),
map(person =>
!pairedSet[0].complete && pairedSet[1].complete
? this.router.navigate(['/home', 'profile', person.id, 'next-steps'])
: pairedSet
)
)
)
);
constructor(private store: Store<any>, private router: Router) {
}
}
当前尝试:
interface StoreState extends MockStoreState {}
describe('Service', () => {
let service: Service;
let mockStore: MockStore<fromStore.State>;
let router: Router;
let mockPersonStateSelector: MemoizedSelector<fromStore.State, fromPersonReducer.PersonState>;
let mockRegistrationSetsStateSelector: MemoizedSelector<
fromStore.State,
fromSetsReducer.SetsState
>;
const initialState: StoreState = { ...mockStoreInitialState };
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
Service,
{ provide: Router, useValue: { navigate: jest.fn() } },
provideMockStore({ initialState })
]
});
mockStore = TestBed.get(Store);
router = TestBed.get(Router);
mockPersonStateSelector = mockStore.overrideSelector(fromStore.selectPersonState, {
...fromPersonReducer.initialState
});
mockSetsStateSelector = mockStore.overrideSelector(fromProfileStore.selectSetsState, {
...fromSetsReducer.initialState
});
service = TestBed.get(Service);
spyOn(mockStore, 'dispatch');
});
it('should be created', () => {
expect(service).toBeTruthy();
});
describe('determineNextSteps$', () => {
describe('when the set completes after a previous incompletion', () => {
it('navigates the user to their next-steps', () => {
mockPersonStateSelector.setResult({ ...fromPersonReducer.initialState, person: { id: '1' } as any });
service['childSet$'] = cold('(ab)', {
a: { name: 'foobar', complete: false },
b: { name: 'foobar', complete: true }
});
expect(service.determineNextSteps$).toBeObservable(cold(''));
expect(router.navigate).toHaveBeenCalledWith(['/home', 'profile', '1', 'next-steps']);
});
});
});
});
这是我的做法:
const testScheduler = new TestScheduler(assertionFunction);
testScheduler.run(({ cold, expectObservable }) => {
const p1 = { name: 'FooBar', id: 1, complete: true };
const p2 = { name: 'FooBar', id: 4, complete: true };
const eventValues = {
a: [p1, { name: 'smthElse', id:2 }],
b: [{ name: 'baz', id: 3 }, p2],
};
const events$ = cold('---a----b|', eventValues);
const expected = ' --------v';
const src$ = merge(
events$.pipe(
tap((parentSets) => mockStore.setState({ parentSets })),
// We just want to subscribe to it, but not interested in any elements
ignoreElements(),
),
// This is the source we're interested in
service.determineNextSteps$
);
// Used in `determineNextSteps$`'s `concatMap`
mockStore.overrideSelector(fromStore.selectPersonState, { person: personObj });
expectObservable(src$).toBe(expected, { v: true });
})
然后您也可以使用相同的模式来测试其他情况。
我正在使用 Angular 并且一直在使用 jasmine-marbles 编写单元测试。这是一个我似乎无法弄清楚的场景。我将如何测试 determineNextSteps$
?除了使用 jasmine-marbles 之外,我也愿意接受其他测试解决方案。
示例:
export class Service {
private person$: Observable<any> = this.store.pipe(
select(fromStore.selectPersonState),
map(({ person }) => person)
);
private parentSetsState$: Observable<any> = this.store.pipe(
select(fromStore.selectParentState)
);
private childSet$: Observable<any> = this.parentSetsState$.pipe(
map(({ parentSets }) =>
parentSets.find(set => set.name === 'FooBar')
)
);
determineNextSteps$: Observable<any> = this.childSet$.pipe(
tap(set =>
this.store.dispatch(
fromStore.determineStep({
set: set
})
)
),
pairwise(),
concatMap(pairedSet =>
this.person$.pipe(
first(),
map(person =>
!pairedSet[0].complete && pairedSet[1].complete
? this.router.navigate(['/home', 'profile', person.id, 'next-steps'])
: pairedSet
)
)
)
);
constructor(private store: Store<any>, private router: Router) {
}
}
当前尝试:
interface StoreState extends MockStoreState {}
describe('Service', () => {
let service: Service;
let mockStore: MockStore<fromStore.State>;
let router: Router;
let mockPersonStateSelector: MemoizedSelector<fromStore.State, fromPersonReducer.PersonState>;
let mockRegistrationSetsStateSelector: MemoizedSelector<
fromStore.State,
fromSetsReducer.SetsState
>;
const initialState: StoreState = { ...mockStoreInitialState };
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
Service,
{ provide: Router, useValue: { navigate: jest.fn() } },
provideMockStore({ initialState })
]
});
mockStore = TestBed.get(Store);
router = TestBed.get(Router);
mockPersonStateSelector = mockStore.overrideSelector(fromStore.selectPersonState, {
...fromPersonReducer.initialState
});
mockSetsStateSelector = mockStore.overrideSelector(fromProfileStore.selectSetsState, {
...fromSetsReducer.initialState
});
service = TestBed.get(Service);
spyOn(mockStore, 'dispatch');
});
it('should be created', () => {
expect(service).toBeTruthy();
});
describe('determineNextSteps$', () => {
describe('when the set completes after a previous incompletion', () => {
it('navigates the user to their next-steps', () => {
mockPersonStateSelector.setResult({ ...fromPersonReducer.initialState, person: { id: '1' } as any });
service['childSet$'] = cold('(ab)', {
a: { name: 'foobar', complete: false },
b: { name: 'foobar', complete: true }
});
expect(service.determineNextSteps$).toBeObservable(cold(''));
expect(router.navigate).toHaveBeenCalledWith(['/home', 'profile', '1', 'next-steps']);
});
});
});
});
这是我的做法:
const testScheduler = new TestScheduler(assertionFunction);
testScheduler.run(({ cold, expectObservable }) => {
const p1 = { name: 'FooBar', id: 1, complete: true };
const p2 = { name: 'FooBar', id: 4, complete: true };
const eventValues = {
a: [p1, { name: 'smthElse', id:2 }],
b: [{ name: 'baz', id: 3 }, p2],
};
const events$ = cold('---a----b|', eventValues);
const expected = ' --------v';
const src$ = merge(
events$.pipe(
tap((parentSets) => mockStore.setState({ parentSets })),
// We just want to subscribe to it, but not interested in any elements
ignoreElements(),
),
// This is the source we're interested in
service.determineNextSteps$
);
// Used in `determineNextSteps$`'s `concatMap`
mockStore.overrideSelector(fromStore.selectPersonState, { person: personObj });
expectObservable(src$).toBe(expected, { v: true });
})
然后您也可以使用相同的模式来测试其他情况。