Last active
May 17, 2019 13:59
-
-
Save evertbouw/94d4a9fdce93b34c31614a673acab73f to your computer and use it in GitHub Desktop.
Writing tests for epics with state$ and using marble diagrams. More information on marble testing can be found at https://github.com/ReactiveX/rxjs/blob/master/doc/marble-testing.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Action } from "redux"; | |
import { ActionsObservable, Epic, StateObservable } from "redux-observable"; | |
import { TestScheduler } from "rxjs/testing"; | |
const assertDeepEquals = (actual: any, expected: any) => { | |
expect(actual).toEqual(expected); | |
}; | |
export const marbleTest = <T extends Action, O extends T = T, S = void, D = any>({ | |
epic, | |
actions, | |
states = "", | |
expected, | |
values, | |
dependencies, | |
}: { | |
epic: Epic<T, O, S, D>; | |
actions: string; | |
states?: string; | |
expected: string; | |
values: { [marble: string]: T | O | S }; | |
dependencies?: D; | |
}) => { | |
const testScheduler = new TestScheduler(assertDeepEquals); | |
testScheduler.run(({ hot, expectObservable }) => { | |
const state$ = new StateObservable<S>(hot<S>(states, <{ [marble: string]: S }>values), <S>values.s); | |
const action$ = new ActionsObservable<T>(hot<T>(actions, <{ [marble: string]: T }>values)); | |
const output$ = epic(action$, state$, <D>dependencies); | |
expectObservable(output$).toBe(expected, values); | |
}); | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { marbleTest } from "./epicMarbleTest"; | |
import { rootReducer, pingEpic, ping, pong, setMessage } from "./reducer"; | |
test("ping epic", () => { | |
const s = rootReducer(undefined, { type: "" }); | |
const t = rootReducer(undefined, setMessage("world")); | |
marbleTest({ | |
epic: pingEpic, | |
actions: "a -- a", | |
states: " -- t", | |
expected: "5s 1 -- 2", | |
values: { | |
a: ping(), | |
s, // s is used as the initial state, doesn't need to go in the marble | |
t, | |
1: pong(s.message), | |
2: pong(t.message) | |
} | |
}); | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { combineReducers, Reducer } from "redux"; | |
import { Epic, ofType } from "redux-observable"; | |
import { mergeMap, take, map, delay } from "rxjs/operators"; | |
export const PING: "PING" = "PING"; | |
export const PONG: "PONG" = "PONG"; | |
export const SET_MESSAGE: "SET_MESSAGE" = "SET_MESSAGE"; | |
export const ping = () => ({ | |
type: PING | |
}); | |
export const pong = (message: string) => ({ | |
type: PONG, | |
payload: message | |
}); | |
export const setMessage = (message: string) => ({ | |
type: SET_MESSAGE, | |
payload: message | |
}); | |
export type AllActions = ReturnType<typeof ping | typeof pong | typeof setMessage>; | |
export type MyReducer<S> = Reducer<S, AllActions>; | |
export const messageReducer: MyReducer<string> = (state = "Hello", action) => { | |
if (action.type === SET_MESSAGE) { | |
return action.payload; | |
} | |
return state; | |
}; | |
export const rootReducer = combineReducers({ | |
message: messageReducer | |
}); | |
export type MyState = ReturnType<typeof rootReducer>; | |
export type MyEpic<O extends AllActions> = Epic<AllActions, O, MyState>; | |
export const pingEpic: MyEpic<ReturnType<typeof pong>> = (action$, state$) => | |
action$.pipe( | |
ofType(PING), | |
mergeMap(() => | |
state$.pipe( | |
take(1), | |
map(state => state.message), | |
delay(5000), | |
map(pong) | |
) | |
) | |
); |
This was for typescript 2.8 I think. In my project I added a typecast like you did, updated the example.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
What versions of the libraries are you using? This doesn't type check for me with
[email protected]
[email protected]
[email protected]
[email protected]
Edit: fixed this by adding
then change the following lines