Feature request: better support for stateful response logic
See original GitHub issue(This might well be a duplicate. If so I’m sorry, it was a tough one to search for.)
Is your feature request related to a problem? Please describe. I have a few mocks where I’m simulating a polling endpoint, where I want the endpoint to return a ‘processing’ response continually for some number of times (a hundred, let’s say), then finally return either ‘success’ or ‘error’.
At the moment, as I understand it, this is a bit unergonomic with MSW. I have to maintain some state in my test code and reference it in the response handlers like this:
function() {
let count = 0;
server.use(
rest.get('/polling-endpoint', (_req, res, ctx) => {
if (count < 100) {
count += 1;
return res(ctx.json(pendingResponse));
}
return res(ctx.json(successResponse));
})
)
}
This is fine, but it makes pulling out the resolver into a utility function that I can use elsewhere quite difficult. Alternatively I can do this, which works but is definitely not ideal:
function() {
const pending = (req, res, ctx) => res.once(ctx.json(pendingResponse));
const ok = (req, res, ctx) => res.once(ctx.json(okResponse));
server.use(
rest.get('/polling-endpoint', pending),
rest.get('/polling-endpoint', pending),
rest.get('/polling-endpoint', pending),
// ...repeat 97 more times
rest.get('/polling-endpoint', ok),
)
}
Describe the solution you’d like
The simplest solution to this specific problem I can think of is to add a .times()
method to res
, which would work like .once()
but allow you to specify the number of times the handler works before its internal shouldSkip
state goes to true.
function() {
const pending = (req, res, ctx) => res.times(100, ctx.json(pendingResponse));
const ok = (req, res, ctx) => res.once(ctx.json(okResponse));
server.use(
rest.get('/polling-endpoint', pending),
rest.get('/polling-endpoint', ok)
)
}
Then .once()
would just be an alias for .times(1)
.
A more complicated but more powerful solution might be to allow you to pass a generator to rest.get
. Then my count
variable from the first example can live inside the handler:
function() {
server.use(
rest.get('/polling-endpoint', function* (_req, res, ctx) {
let count = 0;
if (count < 100) {
count += 1;
yield res(ctx.json(pendingResponse));
}
return res.once(ctx.json(successResponse));
})
)
}
Describe alternatives you’ve considered
I might just be missing something and this isn’t a problem. If so, great!
Additional context Add any other context or screenshots about the feature request here.
Issue Analytics
- State:
- Created 2 years ago
- Reactions:1
- Comments:5 (5 by maintainers)
Top GitHub Comments
Thank you for the effort to add generator support to the response resolver! I’ll review your pull request and we’ll land this great improvement.
Hey, @drivasperez. Thanks for raising this.
Adding support for an API like this is always a delicate matter. One of our core design principles is keeping the library’s public API as plain and straightforward as possible. On the other hand, we want to support some of the common scenarios our users are facing. It comes down to the logic encapsulation and designing an API versatile enough to feel empowering rather than limiting.
When speaking of something like
res.times()
, I can definitely see its practical use, but I can also see how strict that response modifier becomes. If we establish more such modifiers alongsideres.once
, the library starts to give quite too many promises as to what it’s responsible for. In the case ofres.times
, that’d be keeping internal counter and conditional resolver logic for you. Once again, this is practical, but I find it introducing more problems than it solves. Just a fact of us exposing such API leads developers to believe MSW should be responsible for more than request/response pairing, which it really shouldn’t. Then some developers would want to have a custom internal state predicate instead of a simple counter, others would want for the conditional response logic to be integrated in a different way, and so on.What I particularly like about the current API is that it’s designed to be composable. MSW encourages you to encapsulate your custom logic in higher-order resolvers and handlers, if necessary. This introduces 0 overhead, makes you in charge of the logic, and allows you to craft it precisely to your needs.
I’d suggest creating a
withPolling
high-order resolver to achieve what you described above:Please correct me if I’ve misunderstood your expectations towards such logic.