Open tp opened 5 years ago
To support rest argument at the end, I think we'll have to use something like the (previous) calls(returnAfter, 3000, 'test').returning('test')
. (otherwise there is no clear place to put the return value)
Do we need this kind of pattern matching? Also what to do when you only care about the second parameter but not the first one?
I would propose to allow to give mocks functions which will be called with the args and then the user can do some matching on their own. The only downside to that, if a function returns another function, you would also need to wrap them in the test. Though this isn't a typical use case in OMS or the App, I think.
I think matching on the parameters in order is a good start and should work for most functions. If the user really only cares about specific values we could provide an any
value and not look at those in the comparison.
Having a function and force user do the validation will bloat the test code, as for example this example with redux-saga-test-plan
's expectSaga
from OMS:
.provide([
[select(getSessionId), undefined],
{
call(effect, next) {
if (effect.fn === callApi) {
if (effect.args[0].path === 'v2/xxx') {
return {data: {xxx}};
}
}
return next();
},
},
])
With you suggestion the use wouldn't have to check the fn
but still the individual arguments. (Which they could of course do via a helper like equals
).
But even then they'd still be forced to throw
there own error in here, or how else would they communicate: "nope, didn't match"? (Or maybe another Symbol
so the test util can try the next mock).
So overall I think having this build in will result in nicer and short tests code.
I was more thinking of something like:
return expectSaga(watchForUserSelectorToCountIfNotChangedWithing3s)
.withReducer(userReducer)
.withMocks([
call(sleep, Promise.resolve()),
// This function just gets the arguments which were passed to it and can return a value based on that
select(getCount, (id) => id === 5 ? 1 : 4),
// Ignoring parameters
select(getStreamById, (_, filterId) => filterId === 5 ? 1 : 4),
])
Ok, but I still think that makes the test setup a little harder to read if that is the only way / the default.
And also the mentioned drawback of not being able to handle function return values would be an issue for me when treating this as a general library.
Would
select(getStreamById, 5).returning(1)
select(getStreamById, any).returning(4)
be so bad? Another downside with the function would be that it's unclear how often the mock is expected to be used. (I think currently we remove them after each usage).
Lastly, is it really common to run the same function so often that a callback-approach is warranted instead of stating the invocations explicitly? That would to me only seem useful in a loop, and only if the result of the function is trivially generated, such that the logic could be repeated in the test. For all more complex functions, it would probably be really lengthy to provide mock outputs inline in the test.
And then assert that these match before substituting the value.
TODO: Should it throw if it found a matching mock (same function) but different params?