我正在使用jest
来测试一个redux-observable
史诗,它会使用Observable.fromEvent
创建一个内部可观察对象,并在发出动作之前侦听特定的按键。
我正在努力测试内部Observable何时没有收到此特定按键,因此不会发出动作。
使用jest,以下时间超时:
import { Observable, Subject } from 'rxjs'
import { ActionsObservable } from 'redux-observable'
import keycode from 'keycode'
const closeOnEscKeyEpic = action$ =>
action$.ofType('LISTEN_FOR_ESC').switchMapTo(
Observable.fromEvent(document, 'keyup')
.first(event => keycode(event) === 'esc')
.mapTo({ type: 'ESC_PRESSED' })
)
const testEpic = ({ setup, test, expect }) =>
new Promise(resolve => {
const input$ = new Subject()
setup(new ActionsObservable(input$))
.toArray()
.subscribe(resolve)
test(input$)
}).then(expect)
// This times out
it('no action emitted if esc key is not pressed', () => {
expect.assertions(1)
return testEpic({
setup: input$ => closeOnEscKeyEpic(input$),
test: input$ => {
// start listening
input$.next({ type: 'LISTEN_FOR_ESC' })
// press the wrong keys
const event = new KeyboardEvent('keyup', {
keyCode: keycode('p'),
})
const event2 = new KeyboardEvent('keyup', {
keyCode: keycode('1'),
})
global.document.dispatchEvent(event)
global.document.dispatchEvent(event2)
// end test
input$.complete()
},
expect: actions => {
expect(actions).toEqual([])
},
})
})
我的期望是,调用input$.complete()
会导致testEpic
中的承诺解决,但对于此测试则不会。
我觉得我错过了什么。有谁知道为什么这不起作用?
答案 0 :(得分:1)
我还是Rx / RxJS的新手,所以如果这个答案的术语已经关闭,我很抱歉。不过,我能够重现你的场景。
内部可观察对象(Observable.fromEvent
)阻挡了外部可观察对象。 ActionsObservable
上已完成的事件在内部可观察完成之后才会传播。
使用此测试脚本试用以下代码段:
document.getElementById('complete').onclick = onComplete
document.getElementById('listenForEsc').onclick = onListenForEsc
const actions = new Rx.Subject()
const epic = action$ =>
action$.pipe(
Rx.operators.filter(action => action.type === 'LISTEN_FOR_ESC'),
Rx.operators.switchMapTo(
Rx.Observable.fromEvent(document, 'keyup').pipe(
Rx.operators.tap(event => { console.log('keyup: %s', event.keyCode) }),
Rx.operators.first(event => event.keyCode === 27), // escape
Rx.operators.mapTo({ type: 'ESC_PRESSED' }),
)
)
)
epic(actions.asObservable()).subscribe(
action => { console.log('next: %O', action) },
error => { console.log('error: %O', error) },
() => { console.log('complete') },
)
function onListenForEsc() {
actions.next({ type: 'LISTEN_FOR_ESC' })
}
function onComplete() {
actions.complete()
}
<script src="https://unpkg.com/rxjs@5.5.0/bundles/Rx.min.js"></script>
<button id="complete">Complete!</button>
<button id="listenForEsc">Listen for Escape!</button>
switchMapTo
大理石图nor its textual documentation)都没有清楚地表明当源观察源在内部可观察面之前完成时会发生什么。但是,上面的代码片段完全展示了您在Jest测试中观察到的内容。
我相信这会回答你的“为什么”的问题,但我不确定我有一个明确的解决方案。一个选项可以是hook in a cancellation action,并在内部可观察对象上使用takeUntil
。但是,如果只在你的Jest测试中使用它,那可能会感到很尴尬。
我可以看到这个史诗/模式在真实应用中不会出现问题,因为通常情况下,史诗会被创建并订阅一次而不会被取消订阅。但是,根据具体情况(例如,在单个应用程序中多次创建/销毁存储),我可以看到这会导致挂起的订阅和潜在的内存泄漏。很好记住!