Testing expectations after flush
wwjhu opened this issue · comments
If I would like to test a number of expectations after the flush has occurred, what would be the advised approach? Right now I'm disabling autoFlush and call flush manually, but that adds a bit of plumbing to every test that needs it.
If you always want to set autoFlush
to false
, you could wrap and re-export the marbles
function from a module of your own, like this:
import { marbles as _marbles } from "rxjs-marbles";
export function marbles(func: (m: Context, ...rest: any[]) => any): any {
return _marbles((m: Context, ...rest: any[]) => {
m.autoFlush = false;
return func(m, ...rest);
});
}
That's the approach taken within rxjs-marbles
for framework-specific configuration.
That would work although I would look at extending the Context class. Would that be something that you consider as interesting as feature for rxjs-marbles?
it('should ...', marbles((m: Context) => {
// ...
m.onFlush(() => {
// Some extra tests here
})
}));
I'm hesitant to do that. You could always add it to the Context
using the wrapper approach.
I'm curious as to why you would need to perform post-flush
tests so often. What do your tests look like?
The observable chain does calls to methods, in my test case a mocked api call. At the end of the test I want to verify the number of calls made to this api. That is something that can only be checked after the flush.
So those methods are side-effects and their being called cannot otherwise be verified? I.e. via results returned via the observable chain?
Correct, the chain in this case includes an exhaustMap
, filter
and first
/**
* Polls an API at an interval of x milliseconds for a period of y milliseconds. Ensures that only a single poll is
* active at a given point. The interval is therefore a minimum interval: if at the next interval a poll is in
* progress, the poll is skipped for that interval point.
*
* The returned observable emits the first value that passes the provided filter and completes afterwards. If within
* period no values pass the filter, the observable errors with a TimeoutError
*
* Inspired by https://stackoverflow.com/questions/48212752/rxjs-periodic-polling-of-an-endpoint-with-a-variable-response-time#answer-48218063
*
* @param retryInterval interval in milliseconds
* @param retryTimeout timeout in milliseconds
* @param apiFn callback to function providing the observables to poll
* @param filterFn callback to filter function to signal success
* @param scheduler used for testing
* @return {Observable<T>}
*/
static poll<T>(retryInterval: number, retryTimeout: number, apiFn: () => Observable<T>, filterFn: (r: T) => boolean, scheduler?: IScheduler) : Observable<T> {
const timeoutAt = new Date((scheduler ? scheduler.now() : Date.now()) + retryTimeout);
return Observable
.timer(0, retryInterval) // Try to call the api at every interval
.exhaustMap(() => apiFn()) // But avoid multiple calls to the api at the same time
.filter((r: T) => filterFn(r)) // Check whether the caller accepts the value
.first() // We're done when the first item gets through the filter
.timeout(timeoutAt); // Or we're done when we run out of time
}
I'm still hesitant to add it. I kinda prefer flush
to be something you either don't deal with because you write conventional marble tests or something that's completely explicit because you are doing something a little different.
I'm not sure, but I have vague memories of writing tests in which I called flush
multiple times, so onFlush
just seems a little weird to me, at the moment.
I'll give it some more thought, but in the interim, you can always add it yourself using the wrapper mechanism:
import { marbles as _marbles } from "rxjs-marbles";
declare module "rxjs-marbles/context" {
interface Context {
onFlush?: () => void;
}
}
export function marbles(func: (m: Context, ...rest: any[]) => any): any {
return _marbles((m: Context, ...rest: any[]) => {
const result = func(m, ...rest);
if (m.onFlush) { m.onFlush(); }
return result;
});
}
TypeScript's declaration merging makes this sort of thing possible.
I'm still hesitant to add it. I kinda prefer flush to be something you either don't deal with because you write conventional marble tests or something that's completely explicit because you are doing something a little different.
Understood.
Thanks for the snippet and the excellent library.
No worries. There were some errors in that last snippet. They should be fixed, now.