Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

testdouble

Package Overview
Dependencies
Maintainers
1
Versions
115
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

testdouble - npm Package Compare versions

Comparing version 2.0.0-pre.4 to 2.0.0-pre.5

13

CHANGELOG.md
# Change Log
## [v2.0.0-pre.5](https://github.com/testdouble/testdouble.js/tree/v2.0.0-pre.5) (2017-03-13)
[Full Changelog](https://github.com/testdouble/testdouble.js/compare/v2.0.0-pre.4...v2.0.0-pre.5)
**Closed issues:**
- contains not working against IIFE objects [\#192](https://github.com/testdouble/testdouble.js/issues/192)
- Allow matchers inside objects [\#160](https://github.com/testdouble/testdouble.js/issues/160)
- Support invoking callbacks with arbitrary timing [\#106](https://github.com/testdouble/testdouble.js/issues/106)
**Merged pull requests:**
- Async callbacks [\#205](https://github.com/testdouble/testdouble.js/pull/205) ([searls](https://github.com/searls))
## [v2.0.0-pre.4](https://github.com/testdouble/testdouble.js/tree/v2.0.0-pre.4) (2017-03-12)

@@ -4,0 +17,0 @@ [Full Changelog](https://github.com/testdouble/testdouble.js/compare/v2.0.0-pre.3...v2.0.0-pre.4)

77

docs/5-stubbing-results.md

@@ -541,2 +541,79 @@ # Stubbing behavior

#### defer
[Note: you probably don't need this option. Using it everywhere smells of overly
defensive specifications.]
By default, callback stubbings (whether configured via the `td.callback` matcher
or by invoking `thenCallback`) are invoked synchronously. Since testdouble.js is
designed for isolated unit tests, this is usually what you want, because
comprehending and troubleshooting synchronous test scripts will always be much
simpler than asynchronous ones. However, in the event that you want to ensure
the subject isn't inadvertently relying on this synchronous execution of
callbacks, you can ensure those callbacks are scheduled to a later execution
stack by setting the `defer` option to `true`.
Take this example of an erroneously passing test:
```js
// Subject under test
function printBalance (id, fetchBalance, print) {
var balance;
fetchBalance(id, function (er, amount) {
balance = amount
})
print('Your balance is ' + balance)
}
// Test body
var fetchBalance = td.function('.fetchBalance')
var print = td.function('.print')
td.when(fetchBalance(42)).thenCallback(null, 1337)
printBalance(42, fetchBalance, print)
td.verify(print('Your balance is 1337'))
```
The above passes, but suppose that in practice `fetchBalance` is going to
invoke the callback asynchronously—if that's the case, then this passing test
will be lying to us! To guard against this category of test smells, you can set
the `defer` option when stubbing the async dependency, like so:
```
td.when(fetchBalance(42), {defer: true}).thenCallback(null, 1337)
```
Now the test above will fail—shiny! Keep in mind that you'll have to make the
overall test asynchronous (e.g. a `done` callback in Mocha/Jasmine) when using
the `defer` option.
[Note: while this option is also supported for the Promise stubbings
`thenResolve` and `thenReject`, all standard Promise implementations will
already ensure your event handlers will fire asynchronously on a later call
stack.]
#### delay
[Note: you _really_ probably don't need this. You might need `defer` above, but
only reach for this `delay` option when your subject's behavior depends on the
order in which various async operations are completed.]
When using `td.callback`, `thenCallback`, `thenResolve`, or `thenReject`, you
can use the `delay` option to wait a set number of milliseconds before the
operation completes.
Here's a quick and silly example of what this option entails:
```js
var fetch td.function('.fetch')
td.when(fetch('/A'), {delay: 20}).thenCallback(null, 1)
td.when(fetch('/B'), {delay: 10}).thenCallback(null, 2)
td.when(fetch('/C'), {delay: 5}).thenResolve(3)
fetch('/A', function (er, result) {}) // will be invoked 3rd
fetch('/B', function (er, result) {}) // will be invoked 2nd
fetch('/C').then(function (result) {}) // will be invoked 1st
```
## Congratulations!

@@ -543,0 +620,0 @@

292

generated/test/src/callback-test.js

@@ -7,122 +7,206 @@ // Generated by CoffeeScript 1.12.4

});
describe('when', function() {
When(function() {
return this.returnValue = this.testDouble('/foo', (function(_this) {
return function(er, results) {
_this.callbackInvoked = true;
_this.er = er;
return _this.results = results;
};
})(this));
});
context('VERBOSE: using td.callback() as a matcher with a thenReturn chain', function() {
Given(function() {
return td.when(this.testDouble('/foo', td.callback(null, 'some results'))).thenReturn('pandas');
});
Then(function() {
return this.er === null;
});
And(function() {
return this.results === 'some results';
});
return And(function() {
return this.returnValue === 'pandas';
});
});
context('TERSE: use thenCallback chain with td.callback implied as last arg', function() {
Given(function() {
return td.when(this.testDouble('/foo')).thenCallback(null, 'some results');
});
Then(function() {
return this.callbackInvoked = true;
});
And(function() {
return this.er === null;
});
And(function() {
return this.results === 'some results';
});
return And(function() {
return this.returnValue === void 0;
});
});
context('ORDER-EXPLICIT: use td.callback as a marker with a thenCallback chain', function() {
Given(function() {
return td.when(this.testDouble('/foo', td.callback)).thenCallback(null, 'some results');
});
Then(function() {
return this.er === null;
});
And(function() {
return this.results === 'some results';
});
return And(function() {
return this.returnValue === void 0;
});
});
context('EDGE CASE: use td.callback() as a matcher with a thenCallback chain (callback() wins)', function() {
Given(function() {
return td.when(this.testDouble('/foo', td.callback('lolz'))).thenCallback(null, 'some results');
});
Then(function() {
return this.er === 'lolz';
});
return And(function() {
return this.results === void 0;
});
});
context('EDGE CASE: Multiple td.callbacks, some markers and some matchers', function() {
Given(function() {
return td.when(this.testDouble('/bar', td.callback('neat'), td.callback, 'hi')).thenCallback('perfect');
});
return describe('when', function() {
context('callback is synchronous', function() {
When(function() {
return this.testDouble('/bar', ((function(_this) {
return function(cb1arg1) {
_this.cb1arg1 = cb1arg1;
return this.returnValue = this.testDouble('/foo', (function(_this) {
return function(er, results) {
_this.callbackInvoked = true;
_this.er = er;
return _this.results = results;
};
})(this)), ((function(_this) {
return function(cb2arg1) {
_this.cb2arg1 = cb2arg1;
};
})(this)), 'hi');
})(this));
});
Then(function() {
return this.cb1arg1 === 'neat';
context('VERBOSE: using td.callback() as a matcher with a thenReturn chain', function() {
Given(function() {
return td.when(this.testDouble('/foo', td.callback(null, 'some results'))).thenReturn('pandas');
});
Then(function() {
return this.er === null;
});
And(function() {
return this.results === 'some results';
});
return And(function() {
return this.returnValue === 'pandas';
});
});
return And(function() {
return this.cb2arg1 === 'perfect';
context('TERSE: use thenCallback chain with td.callback implied as last arg', function() {
Given(function() {
return td.when(this.testDouble('/foo')).thenCallback(null, 'some results');
});
Then(function() {
return this.callbackInvoked = true;
});
And(function() {
return this.er === null;
});
And(function() {
return this.results === 'some results';
});
return And(function() {
return this.returnValue === void 0;
});
});
});
context('EDGE CASE: use td.callback as a marker with thenReturn (no-arg invocation is made)', function() {
Given(function() {
return td.when(this.testDouble('/foo', td.callback)).thenReturn(null);
context('ORDER-EXPLICIT: use td.callback as a marker with a thenCallback chain', function() {
Given(function() {
return td.when(this.testDouble('/foo', td.callback)).thenCallback(null, 'some results');
});
Then(function() {
return this.er === null;
});
And(function() {
return this.results === 'some results';
});
return And(function() {
return this.returnValue === void 0;
});
});
Then(function() {
return this.er === void 0;
context('EDGE CASE: use td.callback() as a matcher with a thenCallback chain (callback() wins)', function() {
Given(function() {
return td.when(this.testDouble('/foo', td.callback('lolz'))).thenCallback(null, 'some results');
});
Then(function() {
return this.er === 'lolz';
});
return And(function() {
return this.results === void 0;
});
});
And(function() {
return this.results === void 0;
context('EDGE CASE: Multiple td.callbacks, some markers and some matchers', function() {
Given(function() {
return td.when(this.testDouble('/bar', td.callback('neat'), td.callback, 'hi')).thenCallback('perfect');
});
When(function() {
return this.testDouble('/bar', ((function(_this) {
return function(cb1arg1) {
_this.cb1arg1 = cb1arg1;
};
})(this)), ((function(_this) {
return function(cb2arg1) {
_this.cb2arg1 = cb2arg1;
};
})(this)), 'hi');
});
Then(function() {
return this.cb1arg1 === 'neat';
});
return And(function() {
return this.cb2arg1 === 'perfect';
});
});
return And(function() {
return this.callbackInvoked === true;
context('EDGE CASE: use td.callback as a marker with thenReturn (no-arg invocation is made)', function() {
Given(function() {
return td.when(this.testDouble('/foo', td.callback)).thenReturn(null);
});
Then(function() {
return this.er === void 0;
});
And(function() {
return this.results === void 0;
});
return And(function() {
return this.callbackInvoked === true;
});
});
return context('EDGE CASE: thenCallback used but not satisfied', function() {
Given(function() {
return td.when(this.testDouble('/bar')).thenCallback('a-ha');
});
Given(function() {
return td.when(this.testDouble('/bar')).thenReturn('o_O');
});
When(function() {
return this.result = this.testDouble('/bar');
});
return Then(function() {
return this.result === 'o_O';
});
});
});
return context('EDGE CASE: thenCallback used but not satisfied', function() {
Given(function() {
return td.when(this.testDouble('/bar')).thenCallback('a-ha');
return context('callback is asynchronous', function() {
describe('using the defer option', function() {
it('does not invoke synchronously', function(done) {
td.when(this.testDouble('/A'), {
defer: true
}).thenCallback(null, 'B');
this.testDouble('/A', (function(_this) {
return function(er, result) {
_this.callbackInvoked = true;
_this.result = result;
return done();
};
})(this));
if (this.result != null) {
return this.invokedSynchronously = true;
}
});
return afterEach(function() {
expect(this.callbackInvoked).to.eq(true);
expect(this.result).to.eq('B');
return expect(this.invokedSynchronously).not.to.eq(true);
});
});
Given(function() {
return td.when(this.testDouble('/bar')).thenReturn('o_O');
return describe('using the delay option', function() {
if (typeof Promise !== 'function') {
return;
}
it('wraps callbacks and promises in the right order', function(done) {
td.when(this.testDouble('/A'), {
delay: 20
}).thenCallback(null, 'B');
td.when(this.testDouble('/C'), {
delay: 10
}).thenCallback(null, 'D');
td.when(this.testDouble('/E'), {
delay: 15
}).thenResolve('F');
td.when(this.testDouble('/G'), {
delay: 5
}).thenReject('H');
this.results = [];
this.testDouble('/A', (function(_this) {
return function(er, result) {
_this.results.push(result);
if (_this.results.length === 4) {
return done();
}
};
})(this));
this.testDouble('/C', (function(_this) {
return function(er, result) {
_this.results.push(result);
if (_this.results.length === 4) {
return done();
}
};
})(this));
this.testDouble('/E').then((function(_this) {
return function(result) {
_this.results.push(result);
if (_this.results.length === 4) {
return done();
}
};
})(this));
this.testDouble('/G')["catch"]((function(_this) {
return function(error) {
_this.results.push(error);
if (_this.results.length === 4) {
return done();
}
};
})(this));
if (this.results.length > 0) {
return this.invokedSynchronously = true;
}
});
return afterEach(function() {
expect(this.results).to.deep.eq(['H', 'D', 'F', 'B']);
return expect(this.invokedSynchronously).not.to.eq(true);
});
});
When(function() {
return this.result = this.testDouble('/bar');
});
return Then(function() {
return this.result === 'o_O';
});
});
});
return describe('verify???? what would that mean', function() {});
});
}).call(this);
// Generated by CoffeeScript 1.12.4
(function() {
var _, argsMatch, callback, callsStore, config, ensurePromise, executePlan, hasTimesRemaining, invokeCallbackFor, isSatisfied, log, store, stubbedValueFor, stubbingFor;
var _, argsMatch, callCallback, callback, callbackArgs, callsStore, config, createPromise, ensurePromise, executePlan, hasTimesRemaining, invokeCallbackFor, isSatisfied, log, store, stubbedValueFor, stubbingFor,
slice = [].slice;

@@ -47,4 +48,3 @@ _ = require('../util/lodash-wrap');

executePlan = function(stubbing, actualArgs) {
var Promise, value;
Promise = config().promiseConstructor;
var value;
value = stubbedValueFor(stubbing);

@@ -62,11 +62,5 @@ stubbing.callCount += 1;

case "thenResolve":
ensurePromise(Promise);
return new Promise(function(resolve) {
return resolve(value);
});
return createPromise(stubbing, value, true);
case "thenReject":
ensurePromise(Promise);
return new Promise(function(resolve, reject) {
return reject(value);
});
return createPromise(stubbing, value, false);
}

@@ -80,11 +74,46 @@ };

return _.each(stubbing.args, function(expectedArg, i) {
var callbackArgs;
var args;
if (!callback.isCallback(expectedArg)) {
return;
}
callbackArgs = expectedArg.args != null ? expectedArg.args : stubbing.config.plan === 'thenCallback' ? stubbing.stubbedValues : [];
return actualArgs[i].apply(actualArgs, callbackArgs);
args = callbackArgs(stubbing, expectedArg);
return callCallback(stubbing, actualArgs[i], args);
});
};
callbackArgs = function(stubbing, expectedArg) {
if (expectedArg.args != null) {
return expectedArg.args;
} else if (stubbing.config.plan === 'thenCallback') {
return stubbing.stubbedValues;
} else {
return [];
}
};
callCallback = function(stubbing, callback, args) {
if (stubbing.config.delay) {
return _.delay.apply(_, [callback, stubbing.config.delay].concat(slice.call(args)));
} else if (stubbing.config.defer) {
return _.defer.apply(_, [callback].concat(slice.call(args)));
} else {
return callback.apply(null, args);
}
};
createPromise = function(stubbing, value, willResolve) {
var Promise;
Promise = config().promiseConstructor;
ensurePromise(Promise);
return new Promise(function(resolve, reject) {
return callCallback(stubbing, function() {
if (willResolve) {
return resolve(value);
} else {
return reject(value);
}
}, [value]);
});
};
stubbedValueFor = function(stubbing) {

@@ -91,0 +120,0 @@ if (stubbing.callCount < stubbing.stubbedValues.length) {

@@ -27,2 +27,14 @@ 'use strict';

});
Object.defineProperty(exports, 'delay', {
enumerable: true,
get: function get() {
return _lodash.delay;
}
});
Object.defineProperty(exports, 'defer', {
enumerable: true,
get: function get() {
return _lodash.defer;
}
});
Object.defineProperty(exports, 'each', {

@@ -29,0 +41,0 @@ enumerable: true,

// Generated by CoffeeScript 1.12.4
(function() {
module.exports = '2.0.0-pre.4';
module.exports = '2.0.0-pre.5';
}).call(this);
{
"name": "testdouble",
"version": "2.0.0-pre.4",
"version": "2.0.0-pre.5",
"description": "A minimal test double library for TDD with JavaScript",

@@ -5,0 +5,0 @@ "homepage": "https://github.com/testdouble/testdouble.js",

@@ -112,2 +112,4 @@ # testdouble.js

2. [times](docs/5-stubbing-results.md#times)
3. [defer](docs/5-stubbing-results.md#defer)
4. [delay](docs/5-stubbing-results.md#delay)
6. [Verifying invocations](docs/6-verifying-invocations.md#verifying-interactions)

@@ -114,0 +116,0 @@ 1. [td.verify() API](docs/6-verifying-invocations.md#tdverify)

@@ -5,2 +5,4 @@ export {

clone,
delay,
defer,
each,

@@ -7,0 +9,0 @@ every,

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc