Comments (20)
This is already already supported, but if you want to control the callback function then you should configure it separately. Trying to do both in one shot would be too confusing, and any additional parameters to the td.callback
matcher would be passed to the callback function, since all its args are forwarded.
Would this work?
const cb = td.when(td.func(), {delay: 500}).thenCallback(null, 'some data')
td.when(someFunc('arg1', cb)).thenResolve('whatever')
from testdouble.js.
Related: this introduces a situation where thenReturn()
isn't really providing any value at all. Perhaps we should figure out how to let folks call when
without chaining it.
from testdouble.js.
In this example, is there some assumed wiring-up happening so that myFunction
uses the correct fetch
? (edit to clarify: since I am assuming that myFunction
exists in a different file and is using a different version of fetch
in production...that might be a bad assumption though)
from testdouble.js.
@BaseCase yes i was just hand-waving - fixed
from testdouble.js.
td.when(fetch('/some/path', td.callback(null, 'some data'))).done()
or
td.when(fetch('/some/path', td.callback(null, 'some data'))).andNoAndThen()
from testdouble.js.
I suppose alternatively you could just support the omission of a .thenX
as indication there shouldn't be any followup expected.
from testdouble.js.
Jasmine spies default to doing nothing and returning nothing (i.e. undefined
) when first instantiated. That seems like a reasonable thing to do. I’m looking over the implementation of td.when()
and I think it could be easily rengineered to start with a default stubbing of (undefined, config, ‘thenReturn’)
and semantically set rather than add the stubbing if the user explicitly calls one of the then
methods.
You will lose this previously valid pattern:
var fn = td.function();
var stub = fn.when();
fn({ “foo” : “bar” });
stub.thenReturn(“baz”);
…but is anyone using it like that?
from testdouble.js.
Given that the API tries to be pretty English sentencey, maybe something like when(fetch('/some/path', td.callback(null, some data'))).thenCallbackImmediately()
? I dunno, that's pretty cumbersome, but both when
with nothing after it and thenReturn(undefined)
feel kind of awkward to me. Just gut feels though! Don't have any stronger argument than that...
from testdouble.js.
Sometimes (in rare cases) code relies on not having callbacks fire in-thread to work correctly. You might also want .thenCallbackLater()
or .thenCallbackNextTick()
from testdouble.js.
Also 👍 to @davemo's .done()
for simplicity.
from testdouble.js.
when
is supposed to specify how the stubbing will be invoked, and we later specify (with .thenReturn
) how it behaves.
I think the when(fetch(foo, td.callback(null, data)))
seems weird because it's specifying both how fetch will be invoked and how it behaves.
So perhaps:
td.when(fetch('/some/path', td.callback)).thenCallback(null, 'with data')
This way the "how is the stubbing invoked" is still separate from "how does the stubbing behave".
(I'm not sure what purpose or value there is in having td.callback
here, vs a generic function matcher.)
Open to options with streamlined errorBacks. (keeping the node interface would be consistent, but ugly for the 99% case that the callback is a success callback)
td.when(fetch('/some/path', td.callback)).thenCallback('with data') // same as cb(null, 'with data')
td.when(fetch('/some/path', td.callback)).thenErrorback('with err') // same as cb('with err')
from testdouble.js.
FWIW I find @jasonkarns syntax clearest, because of the separation he mentioned.
from testdouble.js.
^ @davemo, that is my inclination. From a user's perspective that would be least surprising i think
from testdouble.js.
@jasonkarns @deanius -- I started with what Jason suggested, but I dislike it for two reasons:
- Having
td.callback
serve as a semantic marker for which argument the callback goes in is weird/awkward and people will get confused (they'd also get confused/upset if we just assumed the last argument was a callback and left it implicit). - Chaining
thenCallback
forfeits the opportunity for the function to also have a meaningful return value, and it's certainly possible someone would have a method that both took a callback param and had a useful return value (esp when the callback is a logical yield and not there for async reasons). I dislike that impedance mismatch after giving it some thought
from testdouble.js.
Why would thenCallback
prevent returning also? If thenCallback()
returns the stubbing, you would just chain thenReturn
on it as well.
td.when(fetch('/some/path', td.callback)).thenCallback(foo).thenReturn(bar)
. This also makes the synchronicity more explicit, anyway. (and i don't think it's a pattern that we would necessarily recommend, so the ugliness of the setup has valuable design pressure)
I'm conflicted on td.callback
as a marker. But since we're supposed to be telling the stubbing how it must be invoked before behaving as prescribed, I would expect the when
invocation to receive standard argument matchers/captors. Whether we have a magic one for callbacks shouldn't matter. It could be as simple as a standard 'any' matcher, or any other function matcher.
from testdouble.js.
@jasonkarns after seeing the complete mess Sinon's chainable API became,
I'm not at all eager to adopt a multi-chaining API in this case.
In the example you gave I'd much rather just treat td.callback
like an
argument matcher or captor and pass it (null, 'foo')
and have that be
that.
On Sat, Feb 13, 2016 at 2:51 PM Jason Karns [email protected]
wrote:
Why would thenCallback prevent returning also? If thenCallback() returns
the stubbing, you would just chain thenReturn on it as well.td.when(fetch('/some/path', td.callback)).thenCallback(foo).thenReturn(bar).
This also makes the synchronicity more explicit, anyway.I'm conflicted on td.callback as a marker. But since we're supposed to be
telling the stubbing how it must be invoked before behaving as prescribed,
I would expect the when invocation to receive standard argument
matchers/captors. Whether we have a magic one for callbacks shouldn't
matter. It could be as simple as a standard 'any' matcher, or any other
function matcher.—
Reply to this email directly or view it on GitHub
#66 (comment)
.
from testdouble.js.
Aside from just wanting things to be terser (and avoiding a special marker
matcher singleton), after thinking a little more about it, another reason
why the impedance mismatch of mapping an argument (of which there are many)
to a test double function (of which there is one in a stubbing), is that a
thenCallback()
API like is being discussed would preclude the
specification of any function that has two callbacks.
If someone has a series(callback1, callback2)
, then
td.when(series(td.callback(null, 'item1'), td.callback(null, 'item2'))
could specify the interaction, but a td.callback
marker + thenCallback
would be unwieldy or unable to specify the interaction.
On Sat, Feb 13, 2016 at 5:47 PM Justin Searls [email protected] wrote:
@jasonkarns after seeing the complete mess Sinon's chainable API became,
I'm not at all eager to adopt a multi-chaining API in this case.In the example you gave I'd much rather just treat
td.callback
like an
argument matcher or captor and pass it(null, 'foo')
and have that be
that.On Sat, Feb 13, 2016 at 2:51 PM Jason Karns [email protected]
wrote:Why would thenCallback prevent returning also? If thenCallback() returns
the stubbing, you would just chain thenReturn on it as well.td.when(fetch('/some/path',
td.callback)).thenCallback(foo).thenReturn(bar). This also makes the
synchronicity more explicit, anyway.I'm conflicted on td.callback as a marker. But since we're supposed to
be telling the stubbing how it must be invoked before behaving as
prescribed, I would expect the when invocation to receive standard
argument matchers/captors. Whether we have a magic one for callbacks
shouldn't matter. It could be as simple as a standard 'any' matcher, or any
other function matcher.—
Reply to this email directly or view it on GitHub
#66 (comment)
.
from testdouble.js.
Hey, a quick update: I implemented this on the thenReturn
or a thenCallback
, but otherwise I implemented basically all the options discussed in this thread.
These should all work (td.callback as a matcher w/ thenReturn):
td.when(fetch('/some/path', td.callback(null, 'some data'))).thenReturn(undefined)
And this (td.callback as position marker):
td.when(fetch('/some/path', td.callback)).thenCallback(null, 'with data')
And also this (implicit td.callback tacked on by the library as last argument when one isn't specified):
td.when(fetch('/some/path')).thenCallback(null, 'with data')
from testdouble.js.
Hi @searls, is there any way force delay on td.callback only? I have an example like:
td.when(someAsyncFunction('arg1'), td.callback(null, 'some data'))).thenResolve('whatever');
I'd like someAsyncFunction
to resolve first and then the data specified in the td.callback should come with a delay.
I've read the delay/defer
sections but it seems delay/defer
is only applicable to the chained method after td.when
Thanks
[EDIT]
So essentially something like:
td.when(someAsyncFunction('arg1'), td.callback(null, 'some data', {delay: 5}))).thenResolve('whatever');
from testdouble.js.
That's what I was looking for, thanks!
from testdouble.js.
Related Issues (20)
- Trying to use testdouble in @web/test-runner fails with error about quibble HOT 1
- ES module replacement changed in Node 18.6 HOT 5
- Using testdouble esm loader with ts-node/esm loader on mocha leads to errors (Node 18.6.0) HOT 24
- function identity is different for modules that are not replaced with testdouble when testdouble is replacing other ESM modules HOT 10
- Importing a mocked ESM module dynamically will throw an error in node 16.17.0 HOT 3
- td.replace support for constructors? HOT 2
- td.replaceEsm() but mocking a global in that module only HOT 2
- Consider relaxing `thenReject` to accept types other than `Error` HOT 2
- How to call-through original function? HOT 1
- Incompatibility with node v20.0.0 HOT 2
- Add docs for using `testdouble` when we have to use another loader (ie `ts-node/esm`) HOT 8
- Where is changelog for 3.19.0 HOT 2
- Get list of mocked modules HOT 7
- Feature request: reset specific module(s) HOT 6
- replaceEsm behaviour HOT 1
- replaceEsm breaks in node20 when other loaders are used HOT 3
- `td.imitate` produces `undefined` for object keys of type `Accessor`
- `td.replaceEsm` does not work when specifier is resolved via loader HOT 1
- Feature request: `td.func().reset()` (reset spy/stub state)
- Suite test:esm fails under Node 21 HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from testdouble.js.