-
-
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
Idea for future milestone(s) #1559
Comments
Ping @sinonjs/core |
Good suggestions, Morgan. Thanks for bringing this up. I also think that the
I agree that immutability is key here. We could allow some "sane" use cases that are currently possible with stubs though. For example, it could be a valid use case to yield and return: sinon.fake({
yields: [null, 42],
returns: true
}) We can check what makes sense and what doesn't. Also, if we support
While I like this idea, it means that calling
I like this a lot. I understand this as a "just let me stick this thing there" utility, right? |
Would that mean that we wouldn't need |
Yeah, that was the idea. Instead of overloading the same method ( |
As you highlighted, the |
It is certainly a breaking change, and should not be introduced lightly. |
When creating a // ~spy, records all calls, has no behaviour
const fake = sinon.fake();
// ~stub, records all calls, returns 'apple pie'
const fake = sinon.fake({
returns: 'apple pie'
}); |
How would you create a stub that does nothing then? |
I am not sure I fully understand your question... but here goes // a fake that has no behaviour
const fake = sinon.fake();
// put it in place of an existing method
sandbox.replace(myObject, 'someMethod', fake); |
Ah, I think I understand what you mean now: A So with that in mind, here is another proposal how we could fold the current const fake = sinon.fake(function () {
// Any custom function
}); The given function would be called by the fake. This API makes it impossible to mix it with other behaviors. In fact, a config object would create a function that implements the specified behavior and then pass it to the fake. The const original = object[method];
const fake = sinon.fake(original);
sandbox.replace(object, method, fake); Basically a one-liner 🤓 |
Yep. Once you simplify things, then you can start re-mixing for fun 🎉 and profit 💰 |
However, if we want to gravitate towards just using |
I'm thinking about the "next" API here. You would need |
I am not sure I follow. Could you elaborate? |
Sure. As I understand your proposal, you want a replacement for the over-complicated |
OK, I think we have a similar understanding 👍 Just to re-iterate, in case we missed something and so that other contributors will have the same understanding. TL;DR
// effectively a spy that has no target
const fake = sinon.fake()
// spy on a function
const fake = sinon.fake(console.log);
const fake = sinon.fake(function() { return 'apple pie'; });
// a shorthand construction of fake with behaviour
const fake = sinon.fake({
returns: 'apple pie'
});
// replacing an existing function with a fake
var fakeLog = sinon.fake();
sandbox.replace(console, 'log', fakeLog); At this state, the proposal only deals with |
Does this mean |
I guess it does overlap somewhat. My primary motivation for this proposal is to have fake functions with immutable behaviour.
If people find that another library better serve their needs, then I am happy the we helped them learn that :) |
But will we keep the spy and stub methods or deprecate them, with some possible reductions in functionality? That was unclear to me. |
Once |
I think we should try our best to provide codemods and great documentation, to help people move their code |
I am working on a branch for the first parts of this (default sandbox). I have refactored the code so that I'll tidy up the commits over the next few days, and then create a branch on this repository for the updated API. |
This is a great idea, very well written btw. I'd also add deprecation notices to stubs and spies. I was also thinking about maybe changing passing an object with keys by passing functions. This would add the following benefits:
Therefore, the API would look like this instead: // It would be cool to allow users to import these using destructuring to make code more concise
import { resolves, rejects, returns } from 'sinon/behaviors';
var fake = sinon.fake(resolves('apple pie'))
var fake = sinon.fake(rejects(new TypeError('no more pie')));
var fake = sinon.fake(rejects('no more pie'));
var fake = sinon.fake(returns('apple pie'));
var fake = sinon.fake(throws(new RangeError('no more pie'));
var fake = sinon.fake(throws('no more pie')); When it comes to implementing this it could just be a matter of returning very simple objects like the ones you are proposing. Then, if we have more than one behavior we can just merge them. Also, when it comes to mixing stuff like Sorry for reviewing this so late. The last few months have been very busy. |
@lucasfcosta check out the PR #1586 |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |
The 5.0.0 previous version is causing problems w/ the later 5.0.0-next.* prerelease versions in package.json because 5.0.0 is greater than any prerelease version. Since 5.0.0 is out there, I think the I noticed this because another package I was using was getting a deprecated msg and its package.json depends on
I wasn't sure if this was worthy of opening a new issue for a prerelease problem, so a comment here seemed safest. |
Another solution would be to release the next major version. What do you think @sinonjs/core? |
@mroderick I can't tell anymore what all the changes for v5 are. From my last tests it worked fine and I'm looking forward to using the new fakes. It's a new major, so hey, ship it 😄 |
There is just one more PR #1764 that I'd like to get merged, before we release the next major version. I've published |
Thanks, I've tested (always good to double check) dependencies in package.json and This was never a big deal, I just thought it was worth making you aware, particularly when I saw that v5 had been in development for a while so I wasn't sure how long until it was released. I think releasing in the near future sounds like a good idea. |
|
Background
sinon.stub
/sandbox.stub
has become the kitchen sink ofconfigurable behaviour with issues that are often difficult to find and fix without regressions.
I think that the root cause of the difficulties is that it
stub
has far too many responsibilities.Further,
stub
also has problematic usage caused by the fact that behaviour is set after it has been created, and can be redefined many times over.And then there are the more confusing scenarios
What does that even do?
Instead of continuing to add more responsibilities to
stub
, I propose that instead we introduce new members tosinon
, which are much narrower in scope.The most important I can think of would be an immutable stand in for a function.
We can then later figure out what we're going to do about properties (as a separate, new, single responsibility member).
sinon.fake
A
fake
(the return value of callingsinon.fake
) is a pure and immutableFunction
. It does one thing, and one thing only. It has the same behaviour on each and every call. Unlikestub
, its behaviour cannot be redefined. If you need different behaviour, make a newfake
.Single responsibility
A fake can have one of these responsibilities
Promise
to a valuePromise
to anError
Error
If you want/need side effects, and you still want the spy interface, then either use the real function, use a
stub
or make a custom functionThrows errors generously
Will be generous with throwing errors when user tries to create/use them in unsupported ways.
Uses the spy API
Except for
.withArgs
, as that violates immutabilityUsage ideas
Synonyms
I don't know if
fake
is the best noun to use here, but I think we should try to stick to the convention of using nouns, and not stray into adjectives or verbs.Proposed API changes
Use a default sandbox
This is something I've been considering for awhile, why don't we make a default sandbox? If people need separate sandboxes, they can still create them.
We should create a default sandbox that is used for all methods that are exposed via
sinon.*
.This means that
sinon.stub
will become the same assandbox.stub
, which will remove the limitation of being able to stub properties usingsinon.stub
.sandbox.replace
Create
sandbox.replace
and use that for all operations that replace anything anywhere. Expose this assinon.replace
and use the default sandbox when used this way.This should probably have some serious input validation, so it'll only replace functions with functions, accessors with accessors, etc.
The text was updated successfully, but these errors were encountered: