RxJs Marble 使用 withLatestFrom 测试 concatMap
RxJs Marble testing concatMap with withLatestFrom
如何对这个 Observable 进行单元测试?
e1.pipe(
concatMap(x => of(x).pipe(withLatestFrom(e2)))
);
以下单元测试失败:
it('test', () => {
const e1 = hot( '--1^---2----3-|');
const e2 = hot( '-a-^-b-----c--|');
const expected = cold( '----x----y-|', {
x: ['2', 'b'],
y: ['3', 'c']
});
const result = e1.pipe(
concatMap(x => of(x).pipe(
withLatestFrom(e2))
)
);
// but this works:
// const result = e1.pipe(withLatestFrom(e2));
expect(result).toBeObservable(expected);
});
弹珠应该怎么写才能通过这个单元测试?我做错了什么?
我希望通过在链中插入 concatMap
运算符(在 withLatestFrom
之前),我还必须以某种方式在弹珠中“标记”它。
在你的真实示例中
e1.pipe(
concatMap(x => of(x).pipe(withLatestFrom(e2)))
);
一切正常,可能是因为是 BehaviorSubject
或 ReplaySubject
,但在您的测试中并非如此。
虽然您使用的是 hot( '-a-^-b-----c--|');
,但这并不意味着您使用的是 BehaviorSubject
。如果我们查看 implementation,我们会看到 HotObservable
扩展了 Subject
class:
export class HotObservable<T> extends Subject<T> implements SubscriptionLoggable { /* ... */ }
这应该有助于理解为什么会这样:
const result = e1.pipe(withLatestFrom(e2));
而这不是:
const result = e1.pipe(
concatMap(x => of(x).pipe(
withLatestFrom(e2))
)
);
在第一个片段中,e2
在 e1
被订阅时被订阅。在第二个中,因为你正在使用 concatMap
,每次 e1
发出时,withLatestFrom(e2))
将被订阅然后取消订阅,因为 complete
通知来自 of(x)
.
考虑到这一点,我的方法是:
注意:我正在使用 rxjs/testing
提供的 built-in 函数
it('test', () => {
// might want to add this in a `beforeEach` function
let testScheduler = new TestScheduler(
(actual, expected) => (console.log({actual, expected}),expect(actual).toEqual(expected))
);
testScheduler.run(({ hot, expectObservable }) => {
const e1 = hot( '--1^---2----3-|');
const e2src = hot( '-a-^-b-----c--|');
const e2 = new BehaviorSubject(undefined);
const result = e1.pipe(
concatMap(x => of(x).pipe(
withLatestFrom(e2))
)
);
const source = merge(
result,
e2src.pipe(
tap(value => e2.next(value)),
// this is important as we're not interesting in `e2src`'s values
// it's just a way to `feed` the `e2` BehaviorSubject
ignoreElements()
)
);
expectObservable(source).toBe('----x----y-|', {
x: ['2', 'b'],
y: ['3', 'c']
});
});
})
如何对这个 Observable 进行单元测试?
e1.pipe(
concatMap(x => of(x).pipe(withLatestFrom(e2)))
);
以下单元测试失败:
it('test', () => {
const e1 = hot( '--1^---2----3-|');
const e2 = hot( '-a-^-b-----c--|');
const expected = cold( '----x----y-|', {
x: ['2', 'b'],
y: ['3', 'c']
});
const result = e1.pipe(
concatMap(x => of(x).pipe(
withLatestFrom(e2))
)
);
// but this works:
// const result = e1.pipe(withLatestFrom(e2));
expect(result).toBeObservable(expected);
});
弹珠应该怎么写才能通过这个单元测试?我做错了什么?
我希望通过在链中插入 concatMap
运算符(在 withLatestFrom
之前),我还必须以某种方式在弹珠中“标记”它。
在你的真实示例中
e1.pipe(
concatMap(x => of(x).pipe(withLatestFrom(e2)))
);
一切正常,可能是因为是 BehaviorSubject
或 ReplaySubject
,但在您的测试中并非如此。
虽然您使用的是 hot( '-a-^-b-----c--|');
,但这并不意味着您使用的是 BehaviorSubject
。如果我们查看 implementation,我们会看到 HotObservable
扩展了 Subject
class:
export class HotObservable<T> extends Subject<T> implements SubscriptionLoggable { /* ... */ }
这应该有助于理解为什么会这样:
const result = e1.pipe(withLatestFrom(e2));
而这不是:
const result = e1.pipe(
concatMap(x => of(x).pipe(
withLatestFrom(e2))
)
);
在第一个片段中,e2
在 e1
被订阅时被订阅。在第二个中,因为你正在使用 concatMap
,每次 e1
发出时,withLatestFrom(e2))
将被订阅然后取消订阅,因为 complete
通知来自 of(x)
.
考虑到这一点,我的方法是:
注意:我正在使用 rxjs/testing
it('test', () => {
// might want to add this in a `beforeEach` function
let testScheduler = new TestScheduler(
(actual, expected) => (console.log({actual, expected}),expect(actual).toEqual(expected))
);
testScheduler.run(({ hot, expectObservable }) => {
const e1 = hot( '--1^---2----3-|');
const e2src = hot( '-a-^-b-----c--|');
const e2 = new BehaviorSubject(undefined);
const result = e1.pipe(
concatMap(x => of(x).pipe(
withLatestFrom(e2))
)
);
const source = merge(
result,
e2src.pipe(
tap(value => e2.next(value)),
// this is important as we're not interesting in `e2src`'s values
// it's just a way to `feed` the `e2` BehaviorSubject
ignoreElements()
)
);
expectObservable(source).toBe('----x----y-|', {
x: ['2', 'b'],
y: ['3', 'c']
});
});
})