-
-
Notifications
You must be signed in to change notification settings - Fork 771
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Return a proper stub for custom subbed methods with callsFake #823
Conversation
Yes, I understand the reasoning behind it and I like the approach you took in the implementation. However, your last point is actually the problem with this: if there are two ways to define behavior, what should happen when So, if there is a way to make all the behaviors work consistently, I'm fine with this. Putting something in that adds to the confusion, not so much. Any ideas? |
@mantoni, great point. Here's the rules in my head for composing behaviors. They may not totally match implementation today, but this is what I'd shoot for:
stub().returns().throws() // only throws, doesn't return
stub().throws().returns(1).returns(2) // only returns 2, doesn't throw
stub().callsFake(->).yields(2) // only calls callback, doesn't call fake
stub().returns(1).onFirstCall().returns(2) // returns 2 then 1 forever
stub().returns(1).onFirstCall().returns(2).throws() // throws then returns 1 forever I imagine last-in-wins rules would be pretty easy to add more uniformly with an internal reset method on behaviors that's called by each of the Happy to add that here or in a separate PR if that direction sounds good to you. |
@cjohansen @mroderick What are your thoughts on this? |
Well this does make sense:
However, I'm not sure I understand the reasoning behind the original example. What were you hoping to achieve with this? logger.error.resetBehavior() I think the point of |
@cjohansen Yes, right. On second thought, |
@cjohansen In the example above I want any calls to In general I like to use this pattern when I'm testing code that needs to handle errors, often integrations with external services. The code under test often has a liberal try catch, with a |
Why aren't you using |
@cjohansen great question! I'd love to use express = require 'express'
request = require 'request'
app = express()
logger =
error: -> throw new Error('Unexpected logger.error')
app.get '/', (req, res) ->
throw new Error 'Oops!'
app.use (err, req, res, next) ->
logger.error err
next()
app.use (err, req, res, next) ->
res.status(500).send()
server = app.listen 3000, ->
server.unref()
# In my test code:
request.get 'http://localhost:3000'
# This will hit logger.error and throw, but express will catch the error and return a 500. |
Looking through older pull requests I have come to this, which has been stuck for half a year. I have to admit I am not getting which problem this is fixing, except for returning a proper stub. Any ideas on how to make this progress? |
@fatso83 thanks for the bump! I'd like to fail a suite that tests an HTTP server through it's API if it calls logger.error. I'd also like test authors to be able to whitelist individual test to not fail the suite by re-defining logger.error. This is all possible today with a layer of tooling on top of sinon, but it looks like there's an opportunity to shore-up sinon and delete my extra layer. As far as moving it forward, I've heard hesitation but no strong objections. I'm happy to put together a PR, but I'd like to hear that maintainers are onboard with direction before I put in the work. |
This PR was quite confusing as the first part did not really address the actual changes, and a lot of the discussion went into how and whys of the supplied test case, which further obscured things. Basically this PR is about replacing: var stub = sinon.stub(foo, 'bar', myCustomFunc); // does not return a real stub with var stub = sinon.stub(foo, 'bar').callsFake(myCustomFunc); // returns a real stub A tad bit more verbose, but to me it seems fine. It's great to remove the inconsistency of todays API, where custom stubs are not true stubs, but to avoid functional duplicity (and inconsistency) we should remove the current way of declaring custom stubs as well. That's a breaking change which should be included before 2.0 is out. The maintainers (including me) seem to agree that the proposed rules for composing behaviors seem sound. They are not directly related and could be merged before this. Would you care to create a PR for that, @hurrymaplelad? Following that, I would like to merge this, but it would need to remove the current custom stubbing method as well, me thinks. |
With this change would I be able to call different fakes based one
Is it possible to reference the arg passed into the stub when the function is actually called many times and you want to deal with it differently on different calls? |
@jnystrom: that's my understanding. I am not totally sure what you are asking concerning the second question. If you create a fake stubbing implementation you have direct access to the argument at all times. |
@fatso83, thank you. Below is an example:
I would like to be able to be able to return something different based on the args passed each time, like:
Is there a way to get the args passed in at each sequential call within the stub? |
Is this still relevant? If so, then it needs a rebase before we can move forwards with it. Perhaps it needs to be re-opened against v1.17 branch? |
Still relevant! I'm back from summer travels now and happy to pick this up. I like the direction proposed in by @fatso83:
I'll create two separate PRs:
|
👍 |
Looks like we'll only need the second PR. All my test cases for consistently composing behaviors pass on master. |
First pass at a codemod script is up: https://github.com/hurrymaplelad/sinon-codemod PR for |
Here's the problem I was having:
The quickest path to implement was to pull in the proposed
callsFake
from #768. I understandcallsFake
style behavior has also been discussed in #278. I hope this PR strikes the right balance of expanding the API a little bit to make the library more productive.Benefits:
sinon.stub(beep, 'boop').onSecondCall().callsFake(x => {value: x})
sinon.stub(beep, 'boop').returns(x)
tosinon.stub(beep, 'boop').callsFake(x => {value: x})
as I'm thinking through my test. Less syntax thrashing.sinon.stub()
now always returns a stubDrawbacks:
callsFake
could be the only syntax after a major version bump.