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

testdouble

Package Overview
Dependencies
Maintainers
2
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 1.2.0 to 1.3.0

.codeclimate.yml

16

docs/7-replacing-dependencies.md

@@ -81,2 +81,18 @@ # Replacing Real Dependencies with Test Doubles

### How module replacement works
Under the hood, testdouble.js uses a module called
[quibble](https://github.com/testdouble/quibble) that facilitates `td.replace`'s
behavior by monkey-patching Node's `require` function (specifically,
`Module._load`). When `td.replace` is invoked for a module, quibble will begin
intercepting any `require` calls made in that file and—bypassing the Node.js
module cache—return a test double instead of the actual module that resolves to
the same absolute path as whatever path was passed to `td.replace`.
As a result, keep in mind that you must **call `td.replace` for each of your
subject's dependencies before you `require` your subject itself**. If you
`require` your subject before calling `td.replace`, it will load normally
(potentially from the module cache) and any calls to `td.replace` will be too
late to have their intended effect).
### Aside: third-party modules

@@ -83,0 +99,0 @@

3

generated/test/general-helper.js

@@ -12,3 +12,4 @@ // Generated by CoffeeScript 1.10.0

afterEach(function() {
return td.reset();
td.reset();
return td.config.reset();
});

@@ -15,0 +16,0 @@

// Generated by CoffeeScript 1.10.0
(function() {
global.td = require('./../src/testdouble');
var sourceDir;
sourceDir = process.env.COVER != null ? process.env.COVER : 'src';
global.requireSource = function(path) {
return require((process.cwd()) + "/" + sourceDir + "/" + path);
};
global.td = requireSource('testdouble');
global.chai = require('chai');

@@ -6,0 +14,0 @@

@@ -7,3 +7,3 @@ // Generated by CoffeeScript 1.10.0

if (NODE_JS) {
return require('../../src/args-match')([expected], [actual], {});
return requireSource('args-match')([expected], [actual], {});
} else {

@@ -197,3 +197,3 @@ return expected.__matches(actual);

};
})(this)), "the contains() matcher only supports strings, arrays, and plain objects");
})(this)), "Error: testdouble.js - td.matchers.contains - this matcher only supports strings, arrays, and plain objects");
});

@@ -200,0 +200,0 @@ });

@@ -86,2 +86,27 @@ // Generated by CoffeeScript 1.10.0

});
describe('Replacing a method on an object instantiated with `new`', function() {
Given(function() {
return this.thing = new this.dependency.thingConstructor();
});
When(function() {
return this.doubleFoo = td.replace(this.thing, 'foo');
});
Then(function() {
return td.explain(this.thing.foo).isTestDouble === true;
});
And(function() {
return this.thing.foo() === void 0;
});
return describe('reset restores it', function() {
When(function() {
return td.reset();
});
Then(function() {
return td.explain(this.thing.foo).isTestDouble === false;
});
return And(function() {
return this.thing.foo() === 'og foo';
});
});
});
describe('Replacing an object / function bag', function() {

@@ -107,7 +132,10 @@ When(function() {

});
describe('Replacing a non-existent property', function() {
describe('Replacing a property that is not an object/function', function() {
Given(function() {
return this.message = 'Error: testdouble.js - td.replace - "badType" property was found, but test double only knows how to replace functions, constructors, & objects containing functions (its value was ';
});
When(function() {
var e, error;
try {
return td.replace(this.dependency, 'notAThing');
return td.replace(this.dependency, 'badType');
} catch (error) {

@@ -118,20 +146,137 @@ e = error;

});
return Then(function() {
return this.error.message === 'td.replace error: No "notAThing" property was found.';
context('a number', function() {
Given(function() {
return this.dependency.badType = 5;
});
return Then(function() {
return this.error.message === this.message + '5).';
});
});
context('a string', function() {
Given(function() {
return this.dependency.badType = "hello";
});
return Then(function() {
return this.error.message === this.message + '"hello").';
});
});
context('null', function() {
Given(function() {
return this.dependency.badType = null;
});
return Then(function() {
return this.error.message === this.message + 'null).';
});
});
return context('undefined', function() {
Given(function() {
return this.dependency.badType = void 0;
});
return Then(function() {
return this.error.message === this.message + 'undefined).';
});
});
});
describe('Replacing a non-existent property', function() {
context('using automatic replacement', function() {
When(function() {
var e, error;
try {
return td.replace(this.dependency, 'notAThing');
} catch (error) {
e = error;
return this.error = e;
}
});
return Then(function() {
return this.error.message === 'Error: testdouble.js - td.replace - No "notAThing" property was found.';
});
});
return context('with manual replacement', function() {
Given(function() {
return this.myFake = td.replace(this.dependency, 'notAThing', 'MY FAKE');
});
Then(function() {
return this.myFake === 'MY FAKE';
});
And(function() {
return this.myFake === this.dependency.notAThing;
});
return context('is deleted following a reset', function() {
Given(function() {
return td.reset();
});
return Then(function() {
return this.dependency.hasOwnProperty('notAThing') === false;
});
});
});
});
return describe('Manually specifying the override', function() {
When(function() {
return this.myDouble = td.replace(this.dependency, 'honk', 'FAKE THING');
Given(function() {
return this.ogWarn = console.warn;
});
Then(function() {
return this.myDouble === 'FAKE THING';
Given(function() {
return this.warnings = [];
});
return And(function() {
return this.myDouble === this.dependency.honk;
Given(function() {
return console.warn = (function(_this) {
return function(msg) {
return _this.warnings.push(msg);
};
})(this);
});
afterEach(function() {
return console.warn = this.ogWarn;
});
context('with a matching type', function() {
Given(function() {
return this.originalHonk = this.dependency.honk;
});
When(function() {
return this.myDouble = td.replace(this.dependency, 'honk', function() {
return 'FAKE THING';
});
});
Then(function() {
return this.myDouble() === 'FAKE THING';
});
And(function() {
return this.myDouble === this.dependency.honk;
});
And(function() {
return this.warnings.length === 0;
});
return context('is restored following a reset', function() {
When(function() {
return td.reset();
});
return Then(function() {
return this.dependency.honk === this.originalHonk;
});
});
});
context('with mismatched types', function() {
Given(function() {
return this.dependency.lol = 5;
});
When(function() {
return td.replace(this.dependency, 'lol', 'foo');
});
return Then(function() {
return this.warnings[0] === "Warning: testdouble.js - td.replace - property \"lol\" 5 (Number) was replaced with \"foo\", which has a different type (String).";
});
});
return context('where the actual is not defined', function() {
When(function() {
return td.replace(this.dependency, 'naw', 'lol');
});
return Then(function() {
return this.warnings.length === 0;
});
});
});
});
return describe('Node.js-specific module replacement', function() {
if (!require('lodash').isFunction(require('quibble'))) {
if (!NODE_JS) {
return;

@@ -138,0 +283,0 @@ }

@@ -9,3 +9,3 @@ // Generated by CoffeeScript 1.10.0

Given(function() {
return this.subject = require('../../../src/stringify/anything');
return this.subject = requireSource('stringify/anything');
});

@@ -12,0 +12,0 @@ Then(function() {

@@ -10,27 +10,27 @@ // Generated by CoffeeScript 1.10.0

Then(function() {
return td.when === require('../../src/when');
return td.when === requireSource('when');
});
Then(function() {
return td.verify === require('../../src/verify');
return td.verify === requireSource('verify');
});
Then(function() {
return td["function"] === require('../../src/function');
return td["function"] === requireSource('function');
});
Then(function() {
return td.object === require('../../src/object');
return td.object === requireSource('object');
});
Then(function() {
return td.matchers === require('../../src/matchers');
return td.matchers === requireSource('matchers');
});
Then(function() {
return td.callback === require('../../src/matchers/callback');
return td.callback === requireSource('matchers/callback');
});
Then(function() {
return td.explain === require('../../src/explain');
return td.explain === requireSource('explain');
});
Then(function() {
return td.reset === require('../../src/reset');
return td.reset === requireSource('reset');
});
Then(function() {
return td.replace === require('../../src/replace/index');
return td.replace === requireSource('replace/index');
});

@@ -37,0 +37,0 @@ return Then(function() {

@@ -136,3 +136,3 @@ // Generated by CoffeeScript 1.10.0

};
})(this)), "No test double invocation detected for `verify()`.\n\n Usage:\n verify(myTestDouble('foo'))");
})(this)), "Error: testdouble.js - td.verify - No test double invocation detected for `verify()`.\n\n Usage:\n verify(myTestDouble('foo'))");
});

@@ -139,0 +139,0 @@ });

@@ -179,2 +179,19 @@ // Generated by CoffeeScript 1.10.0

});
describe('stubbing error, no invocation found', function() {
Given(function() {
return td.reset();
});
Given(function() {
var e, error;
try {
return td.when().thenReturn('hi');
} catch (error) {
e = error;
return this.error = e;
}
});
return Then(function() {
return this.error.message === "Error: testdouble.js - td.when - No test double invocation call detected for `when()`.\n\n Usage:\n when(myTestDouble('foo')).thenReturn('bar')";
});
});
return describe('config object', function() {

@@ -181,0 +198,0 @@ describe('ignoring extra arguments', function() {

// Generated by CoffeeScript 1.10.0
(function() {
var _, create, stringifyArguments;
var _, create, log, stringifyArguments;
_ = require('lodash');
log = require('../log');
create = require('./create');

@@ -66,3 +68,3 @@

} else {
throw new Error("the contains() matcher only supports strings, arrays, and plain objects");
return log.error("td.matchers.contains", "this matcher only supports strings, arrays, and plain objects");
}

@@ -69,0 +71,0 @@ });

// Generated by CoffeeScript 1.10.0
(function() {
var _, isConstructor, object, tdFunction, wrapWithConstructor;
var _, isConstructor, log, object, stringifyAnything, tdFunction, wrapWithConstructor;

@@ -11,2 +11,4 @@ _ = require('lodash');

log = require('../log');
isConstructor = require('./is-constructor');

@@ -16,7 +18,11 @@

stringifyAnything = require('../stringify/anything');
module.exports = function(realThing, optionalName) {
if (isConstructor(realThing) || _.isPlainObject(realThing)) {
return object(realThing);
} else if (_.isFunction(realThing)) {
return tdFunction((realThing != null ? realThing.name : void 0) ? realThing.name : optionalName);
} else {
return tdFunction((realThing != null ? realThing.name : void 0) ? realThing.name : optionalName);
return log.error("td.replace", "\"" + optionalName + "\" property was found, but test double only knows how to replace functions, constructors, & objects containing functions (its value was " + (stringifyAnything(realThing)) + ").");
}

@@ -23,0 +29,0 @@ };

// Generated by CoffeeScript 1.10.0
(function() {
var ensurePropertyExists, imitate, isConstructor, reset, wrapIfNeeded, wrapWithConstructor;
var _, imitate, isConstructor, log, reset, stringifyAnything, warnIfTypeMismatch, wrapIfNeeded, wrapWithConstructor;

@@ -11,22 +11,30 @@ imitate = require('./imitate');

log = require('../log');
reset = require('../reset');
stringifyAnything = require('../stringify/anything');
_ = require('lodash');
module.exports = function(object, property, manualReplacement) {
var fakeThing, realThing;
ensurePropertyExists(object, property);
var fakeThing, isManual, realThing, realThingExists;
isManual = arguments.length > 2;
realThingExists = object[property] || object.hasOwnProperty(property);
if (!isManual && !realThingExists) {
log.error("td.replace", "No \"" + property + "\" property was found.");
}
realThing = object[property];
fakeThing = arguments.length > 2 ? manualReplacement : imitate(realThing, property);
fakeThing = isManual ? (warnIfTypeMismatch(property, manualReplacement, realThing), manualReplacement) : imitate(realThing, property);
object[property] = wrapIfNeeded(fakeThing, realThing);
reset.onNextReset(function() {
return object[property] = realThing;
if (realThingExists) {
return object[property] = realThing;
} else {
return delete object[property];
}
});
object[property] = wrapIfNeeded(fakeThing, realThing);
return fakeThing;
};
ensurePropertyExists = function(object, property) {
if (!object.hasOwnProperty(property)) {
throw new Error("td.replace error: No \"" + property + "\" property was found.");
}
};
wrapIfNeeded = function(fakeThing, realThing) {

@@ -40,2 +48,14 @@ if (isConstructor(realThing)) {

warnIfTypeMismatch = function(property, fakeThing, realThing) {
var fakeType, realType;
if (realThing === void 0) {
return;
}
fakeType = typeof fakeThing;
realType = typeof realThing;
if (fakeType !== realType) {
return log.warn("td.replace", "property \"" + property + "\" " + (stringifyAnything(realThing)) + " (" + (_.capitalize(realType)) + ") was replaced with " + (stringifyAnything(fakeThing)) + ", which has a different type (" + (_.capitalize(fakeType)) + ").");
}
};
}).call(this);

@@ -13,2 +13,6 @@ // Generated by CoffeeScript 1.10.0

store.onReset(function() {
return lastCall = null;
});
module.exports = {

@@ -15,0 +19,0 @@ log: function(testDouble, args, context) {

// Generated by CoffeeScript 1.10.0
(function() {
var _, globalStore, initialEntryFor;
var EventEmitter, _, globalStore, initialEntryFor, storeEmitter;
_ = require('lodash');
EventEmitter = require('events');
storeEmitter = new EventEmitter();
globalStore = [];
module.exports = {
onReset: function(func) {
return storeEmitter.on('reset', func);
},
reset: function() {
return globalStore = [];
globalStore = [];
return storeEmitter.emit('reset');
},

@@ -13,0 +21,0 @@ "for": function(testDouble, createIfNew) {

@@ -9,6 +9,7 @@ // Generated by CoffeeScript 1.10.0

matchers: require('./matchers/index'),
callback: require('./matchers/callback'),
replace: require('./replace/index'),
explain: require('./explain'),
replace: require('./replace/index'),
reset: require('./reset'),
config: require('./config'),
callback: require('./matchers/callback'),
version: require('./version')

@@ -15,0 +16,0 @@ };

// Generated by CoffeeScript 1.10.0
(function() {
var _, callsStore, invocationSummary, store, stringifyArgs, stringifyName, timesMessage, unsatisfiedErrorMessage;
var _, callsStore, invocationSummary, log, store, stringifyArgs, stringifyName, timesMessage, unsatisfiedErrorMessage;

@@ -13,2 +13,4 @@ _ = require('lodash');

log = require('./log');
module.exports = function(__userDoesPretendInvocationHere__, config) {

@@ -23,6 +25,6 @@ var last;

} else {
throw new Error(unsatisfiedErrorMessage(last.testDouble, last.args, config));
return log.fail(unsatisfiedErrorMessage(last.testDouble, last.args, config));
}
} else {
throw new Error("No test double invocation detected for `verify()`.\n\n Usage:\n verify(myTestDouble('foo'))");
return log.error("td.verify", "No test double invocation detected for `verify()`.\n\n Usage:\n verify(myTestDouble('foo'))");
}

@@ -29,0 +31,0 @@ };

// Generated by CoffeeScript 1.10.0
(function() {
module.exports = '1.2.0';
module.exports = '1.3.0';
}).call(this);
// Generated by CoffeeScript 1.10.0
(function() {
var _, addStubbing, callback, calls, concatImpliedCallback, stubbings,
var _, addStubbing, callback, calls, concatImpliedCallback, log, stubbings,
slice = [].slice;

@@ -14,2 +14,4 @@

log = require('./log');
module.exports = function(__userDoesPretendInvocationHere__, config) {

@@ -52,3 +54,3 @@ if (config == null) {

} else {
throw new Error("No test double invocation call detected for `when()`.\n\n Usage:\n when(myTestDouble('foo')).thenReturn('bar')");
return log.error("td.when", "No test double invocation call detected for `when()`.\n\n Usage:\n when(myTestDouble('foo')).thenReturn('bar')");
}

@@ -55,0 +57,0 @@ };

{
"name": "testdouble",
"version": "1.2.0",
"version": "1.3.0",
"description": "A minimal test double library for TDD with JavaScript",

@@ -17,3 +17,4 @@ "homepage": "https://github.com/testdouble/testdouble.js",

"scripts": {
"clean": "rm -rf generated dist lib && git checkout -- dist",
"clean:dist": "git checkout -- dist",
"clean": "rm -rf generated dist lib .coverage && npm run clean:dist",
"compile:node": "coffee --output lib --compile src",

@@ -23,3 +24,7 @@ "compile:test": "coffee --output generated/test --compile test",

"compile": "npm run compile:node && npm run compile:test && npm run compile:browser",
"test": "mocha --ui mocha-given --reporter $npm_package_config_mocha_reporter --compilers coffee:coffee-script --recursive test/node-helper.coffee test/src",
"test": "mocha --ui mocha-given --reporter $npm_package_config_mocha_reporter --compilers coffee:coffee-script $EXTRA_MOCHA_ARGS --recursive test/node-helper.coffee test/src",
"test:cover:instrument": "istanbul instrument lib -o .coverage/lib",
"test:cover:run": "EXTRA_MOCHA_ARGS=\"--reporter mocha-istanbul\" COVER=.coverage/lib ISTANBUL_REPORT_DIR=.coverage ISTANBUL_REPORTERS=lcov,html npm run test",
"test:cover:report": "codeclimate-test-reporter < .coverage/lcov.info",
"test:cover": "npm run compile:node && npm run test:cover:instrument && npm run test:cover:run",
"test:browser": "npm run compile && testem ci",

@@ -31,3 +36,3 @@ "test:example:webpack": "cd examples/webpack && npm i && npm test & cd ../..",

"test:all": "npm run test --testdouble:mocha_reporter=tap && testem ci && npm run test:example",
"test:ci": "npm run clean && npm run compile && npm run test:all",
"test:ci": "npm run clean && npm run compile && npm run test:all && npm run clean:dist",
"test:debug": "npm test -- --debug-brk",

@@ -52,7 +57,10 @@ "version:write": "echo \"module.exports = '$npm_package_version'\" > src/version.coffee",

"chai": "^3.2.0",
"codeclimate-test-reporter": "^0.3.1",
"coffee-script": "^1.10.0",
"disc": "^1.3.2",
"headerify": "^1.0.1",
"istanbul": "^0.4.2",
"mocha": "^2.3.1",
"mocha-given": "^0.1.3",
"mocha-istanbul": "arikon/mocha-istanbul",
"testem": "^0.9.4"

@@ -59,0 +67,0 @@ },

# testdouble.js
[![Build Status](https://secure.travis-ci.org/testdouble/testdouble.js.svg)](http://travis-ci.org/testdouble/testdouble.js) [![npmjs](https://img.shields.io/badge/npm-testdouble-red.svg)](https://www.npmjs.com/package/testdouble)
[![Test Coverage](https://codeclimate.com/github/testdouble/testdouble.js/badges/coverage.svg)](https://codeclimate.com/github/testdouble/testdouble.js/coverage)

@@ -25,13 +26,34 @@ Welcome! Are you writing JavaScript tests and in the market for a mocking library to

Before diving into our in-depth docs, here are a couple demo GIFs of the basic
uses:
Before diving into our in-depth docs, here is a quick intro of the basic uses:
### Stubbing return values for functions
![simple stubbing example animation](docs/img/stub.gif)
```js
var td = require('testdouble');
var fetch = td.function();
td.when(fetch(42)).thenReturn('Jane User');
fetch(42); // -> 'Jane User'
```
### Verifying a function was invoked
![simple verification of a function invocation](docs/img/verify.gif)
```js
var td = require('testdouble');
var save = td.function('.save');
save(41, 'Jane');
td.verify(save(41, 'Jill'));
//
// Error: Unsatisfied verification on test double `.save`.
//
// Wanted:
// - called with `(41, "Jill")`.
//
// But was actually called:
// - called with `(41, "Jane")`.
```
## Docs

@@ -38,0 +60,0 @@

Sorry, the diff of this file is not supported yet

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

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

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

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

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

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