testdouble
Advanced tools
Comparing version 0.7.2 to 0.7.3
@@ -101,68 +101,99 @@ // Generated by CoffeeScript 1.10.0 | ||
describe('.contains', function() { | ||
Then(function() { | ||
return this.matchers.contains('a').__matches(['a', 'b', 'c']) === true; | ||
context('strings', function() { | ||
Then(function() { | ||
return this.matchers.contains('bar').__matches('foobarbaz') === true; | ||
}); | ||
Then(function() { | ||
return this.matchers.contains('biz').__matches('foobarbaz') === false; | ||
}); | ||
return Then(function() { | ||
return shouldThrow(((function(_this) { | ||
return function() { | ||
return _this.matchers.contains(48).__matches(); | ||
}; | ||
})(this)), "the contains() matcher only supports strings, arrays, and plain objects"); | ||
}); | ||
}); | ||
Then(function() { | ||
return this.matchers.contains('a', 'c').__matches(['a', 'b', 'c']) === true; | ||
context('arrays', function() { | ||
Then(function() { | ||
return this.matchers.contains('a').__matches(['a', 'b', 'c']) === true; | ||
}); | ||
Then(function() { | ||
return this.matchers.contains('a', 'c').__matches(['a', 'b', 'c']) === true; | ||
}); | ||
Then(function() { | ||
return this.matchers.contains(['a', 'c']).__matches(['a', 'b', 'c']) === false; | ||
}); | ||
Then(function() { | ||
return this.matchers.contains(['a', 'c']).__matches([1, ['a', 'c'], 4]) === true; | ||
}); | ||
return Then(function() { | ||
return this.matchers.contains(['a', 'c']).__matches(['a', 'b', 'z']) === false; | ||
}); | ||
}); | ||
Then(function() { | ||
return this.matchers.contains(['a', 'c']).__matches(['a', 'b', 'c']) === false; | ||
return context('objects', function() { | ||
Then(function() { | ||
return this.matchers.contains({ | ||
foo: 'bar', | ||
baz: 42 | ||
}).__matches({ | ||
foo: 'bar', | ||
baz: 42, | ||
stuff: this | ||
}) === true; | ||
}); | ||
Then(function() { | ||
return this.matchers.contains({ | ||
foo: 'bar', | ||
lol: 42 | ||
}).__matches({ | ||
foo: 'bar', | ||
baz: 42 | ||
}) === false; | ||
}); | ||
Then(function() { | ||
return this.matchers.contains({ | ||
lol: { | ||
deep: [4, 2] | ||
} | ||
}).__matches({ | ||
lol: { | ||
deep: [4, 2], | ||
other: "stuff" | ||
} | ||
}) === true; | ||
}); | ||
Then(function() { | ||
return this.matchers.contains({ | ||
deep: { | ||
thing: 'stuff' | ||
} | ||
}).__matches({}) === false; | ||
}); | ||
Then(function() { | ||
return this.matchers.contains({ | ||
deep: { | ||
thing: 'stuff' | ||
} | ||
}).__matches({ | ||
deep: { | ||
thing: 'stuff', | ||
shallow: 5 | ||
} | ||
}) === true; | ||
}); | ||
return Then(function() { | ||
return this.matchers.contains({ | ||
container: { | ||
size: 'S' | ||
} | ||
}).__matches({ | ||
ingredient: 'beans', | ||
container: { | ||
type: 'cup', | ||
size: 'S' | ||
} | ||
}) === true; | ||
}); | ||
}); | ||
Then(function() { | ||
return this.matchers.contains(['a', 'c']).__matches([1, ['a', 'c'], 4]) === true; | ||
}); | ||
Then(function() { | ||
return this.matchers.contains(['a', 'c']).__matches(['a', 'b', 'z']) === false; | ||
}); | ||
Then(function() { | ||
return this.matchers.contains({ | ||
foo: 'bar', | ||
baz: 42 | ||
}).__matches({ | ||
foo: 'bar', | ||
baz: 42, | ||
stuff: this | ||
}) === true; | ||
}); | ||
Then(function() { | ||
return this.matchers.contains({ | ||
foo: 'bar', | ||
lol: 42 | ||
}).__matches({ | ||
foo: 'bar', | ||
baz: 42 | ||
}) === false; | ||
}); | ||
Then(function() { | ||
return this.matchers.contains({ | ||
lol: { | ||
deep: [4, 2] | ||
} | ||
}).__matches({ | ||
lol: { | ||
deep: [4, 2], | ||
other: "stuff" | ||
} | ||
}) === true; | ||
}); | ||
Then(function() { | ||
return this.matchers.contains({ | ||
deep: { | ||
thing: 'stuff' | ||
} | ||
}).__matches({}) === false; | ||
}); | ||
Then(function() { | ||
return this.matchers.contains('bar').__matches('foobarbaz') === true; | ||
}); | ||
Then(function() { | ||
return this.matchers.contains('biz').__matches('foobarbaz') === false; | ||
}); | ||
return Then(function() { | ||
return shouldThrow(((function(_this) { | ||
return function() { | ||
return _this.matchers.contains(48).__matches(); | ||
}; | ||
})(this)), "the contains() matcher only supports strings, arrays, and plain objects"); | ||
}); | ||
}); | ||
@@ -169,0 +200,0 @@ return describe('argThat', function() { |
{ | ||
"name": "testdouble", | ||
"version": "0.7.2", | ||
"version": "0.7.3", | ||
"description": "A minimal test double library for TDD with JavaScript", | ||
@@ -11,3 +11,3 @@ "homepage": "https://github.com/testdouble/testdouble.js", | ||
}, | ||
"main": "generated/testdouble.js", | ||
"main": "lib/testdouble.js", | ||
"config": { | ||
@@ -18,15 +18,14 @@ "build_file": "dist/testdouble.js", | ||
"scripts": { | ||
"clean": "rm -rf generated dist && git checkout -- dist", | ||
"clean": "rm -rf generated dist lib && git checkout -- dist", | ||
"start": "testem", | ||
"compile": "coffee --output generated --compile lib", | ||
"compile:tests": "coffee --output generated/test --compile test", | ||
"compile:node": "coffee --output lib --compile src", | ||
"compile:test": "coffee --output generated/test --compile test", | ||
"compile:browser": "browserify . --standalone testdouble --outfile $npm_package_config_build_file --ignore 'quibble' -p headerify", | ||
"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/lib", | ||
"test:ci": "npm run test --testdouble:mocha_reporter=tap && testem ci", | ||
"test:examples": "cd examples/node && npm i && npm test && cd ../..", | ||
"test:example": "cd examples/node && npm i && npm test && cd ../..", | ||
"test:ci": "npm run test --testdouble:mocha_reporter=tap && testem ci && npm run test:example", | ||
"test:debug": "npm test -- --debug-brk", | ||
"prebuild": "npm run compile", | ||
"build": "browserify . --standalone testdouble --outfile $npm_package_config_build_file --ignore 'quibble'", | ||
"audit:disc": "npm run build -- --full-paths && discify $npm_package_config_build_file --open", | ||
"preversion": "git pull --rebase && npm run test:ci", | ||
"version": "npm run build && git add dist", | ||
"version": "npm run compile && git add dist", | ||
"postversion": "git push && git push --tags && npm publish", | ||
@@ -36,3 +35,3 @@ "prepublish": "npm run compile" | ||
"browser": { | ||
"./generated/replace.js": "./generated/replace.browser.js" | ||
"./lib/replace.js": "./lib/replace.browser.js" | ||
}, | ||
@@ -47,3 +46,3 @@ "browserify": { | ||
"lodash": "^3.10.1", | ||
"quibble": "^0.2.1" | ||
"quibble": "^0.3.0" | ||
}, | ||
@@ -56,2 +55,3 @@ "devDependencies": { | ||
"envify": "^3.4.0", | ||
"headerify": "^1.0.1", | ||
"mocha": "^2.3.1", | ||
@@ -58,0 +58,0 @@ "mocha-given": "^0.1.3", |
462
README.md
# testdouble.js | ||
[![Build Status](https://secure.travis-ci.org/testdouble/testdouble.js.svg)](http://travis-ci.org/testdouble/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) | ||
The goal of this project is to provide a test-framework-agnostic test double library for JavaScript which mirrors [Mockito](http://mockito.org) pretty closely. That means each Test Double created by the library will be a spy that is also capable of stubbing values. Other conveniences have been and will be added, but only to the extent they benefit an isolated TDD workflow. | ||
Welcome! Are you writing JavaScript tests and in the market for a mocking library to | ||
fake out real things for you? testdouble.js is an opinionated, carefully-designed | ||
test double library maintained by, oddly enough, a software agency that's also | ||
named [Test Double](http://testdouble.com). | ||
If you need a robust test double library that's designed to cover every possible use case, we recommend checking out [Sinon.JS](http://sinonjs.org). | ||
If you practice test-driven development, testdouble.js was designed to promote | ||
terse, clear, and easy-to-understand tests. There's an awful lot to cover, so | ||
please take some time and enjoy our documentation, which itself is designed to | ||
show you how to make the most out of test doubles in your tests. | ||
## Install | ||
## Docs | ||
### Node.js | ||
All of our docs are in the [docs/](docs/) directory inside this repository and | ||
numbered for easy reading in the priority-order we anticipate people needing them. | ||
Here's a rough outline: | ||
``` | ||
npm install testdouble --save-dev | ||
``` | ||
1. [Installation](docs/1-installation.md#installing-testdoublejs) | ||
1. [for Node.js](docs/1-installation.md#for-use-in-nodejs-or-browserify) | ||
2. [for browsers](docs/1-installation.md#for-use-in-browsers) | ||
3. [initial configuration](docs/1-installation.md#configuring-testdoublejs-setting-up-in-your-test-suite) | ||
2. [Purpose of testdouble.js](docs/2-howto-purpose.md#background) | ||
1. [in unit tests](docs/2-howto-purpose.md#test-doubles-and-unit-tests) | ||
2. [in integration tests](docs/2-howto-purpose.md#test-doubles-and-integration-tests) | ||
3. [Getting started tutorial](docs/3-getting-started.md#getting-started) | ||
4. [Creating test doubles](docs/4-creating-test-doubles.md#creating-test-doubles) | ||
1. [test double functions with `td.function()`](docs/4-creating-test-doubles.md#testdoublefunctionname) | ||
2. [test double objects with `td.object()`](docs/4-creating-test-doubles.md#testdoubleobject) | ||
1. [objects that mirror a constructor function](docs/4-creating-test-doubles.md#objectsomeconstructorfunction) | ||
2. [objects that mirror an object of functions](docs/4-creating-test-doubles.md#objectsomeobjectwithfunctions) | ||
3. [object of functions for an array of names](docs/4-creating-test-doubles.md#objectfunctionnames) | ||
4. [object of any functions using ES2015 Proxy](docs/4-creating-test-doubles.md#objectobjectname) | ||
5. [Stubbing responses](docs/5-stubbing-results.md#stubbing-behavior) | ||
1. [td.when() API](docs/5-stubbing-results.md#testdoublewhen) | ||
2. [equality argument matching](docs/5-stubbing-results.md#simple-precise-argument-stubbing) | ||
3. [one-liner stubbings](docs/5-stubbing-results.md#one-liner-stubbings) | ||
4. [stubbing sequential return values](docs/5-stubbing-results.md#stubbing-sequential-return-values) | ||
5. [argument matchers](docs/5-stubbing-results.md#loosening-stubbings-with-argument-matchers) | ||
1. [td.matchers.anything()](docs/5-stubbing-results.md#testdoublematchersanything) | ||
2. [td.matchers.isA()](docs/5-stubbing-results.md#testdoublematchersisa) | ||
3. [td.matchers.contains()](docs/5-stubbing-results.md#testdoublematcherscontains) | ||
1. [matching strings](docs/5-stubbing-results.md#strings) | ||
2. [matching arrays](docs/5-stubbing-results.md#arrays) | ||
3. [matching objects](docs/5-stubbing-results.md#objects) | ||
4. [td.matchers.argThat()](docs/5-stubbing-results.md#testdoublematchersargthat) | ||
6. [Configuring stubbings](docs/5-stubbing-results.md#configuring-stubbings) | ||
1. [ignoreExtraArgs](docs/5-stubbing-results.md#ignoreextraargs) | ||
2. [times](docs/5-stubbing-results.md#times) | ||
6. [Verifying invocations](docs/6-verifying-invocations.md#verifying-interactions) | ||
1. [td.verify() API](docs/6-verifying-invocations.md#testdoubleverify) | ||
2. [equality argument matching](docs/6-verifying-invocations.md#arguments) | ||
3. [argument matchers](docs/6-verifying-invocations.md#relaxing-verifications-with-argument-matchers) | ||
1. [td.matchers.anything()](docs/6-verifying-invocations.md#testdoublematchersanything) | ||
2. [td.matchers.isA()](docs/6-verifying-invocations.md#testdoublematchersisa) | ||
3. [td.matchers.contains()](docs/6-verifying-invocations.md#testdoublematcherscontains) | ||
1. [matching strings](docs/6-verifying-invocations.md#strings) | ||
2. [matching arrays](docs/6-verifying-invocations.md#arrays) | ||
3. [matching objects](docs/6-verifying-invocations.md#objects) | ||
4. [td.matchers.argThat()](docs/6-verifying-invocations.md#testdoublematchersargthat) | ||
4. [Argument captors](docs/6-verifying-invocations.md#multi-phase-assertions-with-argument-captors) | ||
5. [Configuring verifications](docs/6-verifying-invocations.md#configuring-verifications) | ||
1. [ignoreExtraArgs](docs/6-verifying-invocations.md#ignoreextraargs) | ||
2. [times](docs/6-verifying-invocations.md#times) | ||
7. [Replacing dependencies with test doubles](docs/7-replacing-dependencies.md#replacing-dependencies-with-test-doubles) | ||
1. [for Node.js](docs/7-replacing-dependencies.md#nodejs) | ||
1. [td.replace() API](docs/7-replacing-dependencies.md#testdoublereplacerelativepath-faketoreplace) | ||
1. [Replacing plain functions](docs/7-replacing-dependencies.md#replacing-a-module-that-exports-a-plain-function) | ||
2. [Replacing plain objects](docs/7-replacing-dependencies.md#replacing-a-module-that-exports-a-plain-object) | ||
3. [Replacing a constructor function](docs/7-replacing-dependencies.md#replacing-a-module-that-exports-a-constructor-function) | ||
4. [Replacing a module that doesn't exist (yet)](docs/7-replacing-dependencies.md#replacing-a-module-that-doesnt-exist-yet) | ||
2. [for Browser JS](docs/7-replacing-dependencies.md#browser) | ||
8. [Writing custom argument matchers](docs/8-custom-matchers.md#custom-argument-matchers) | ||
9. [Debugging with testdouble.js](docs/9-debugging.md#debugging-with-testdoublejs) | ||
1. [td.explain() API](docs/9-debugging.md#testdoubleexplainsometestdouble) | ||
10. [Plugins](docs/A-plugins.md#plugins) | ||
1. [testdouble-chai](https://github.com/basecase/testdouble-chai) | ||
2. [testdouble-jasmine](https://github.com/testdouble/testdouble.js/issues/41) | ||
11. [Frequently Asked Questions](docs/B-frequently-asked-questions.md#frequently-asked-questions) | ||
1. [Why doesn't `td.replace()` work with external CommonJS modules?](docs/B-frequently-asked-questions.md#why-doesnt-tdreplace-work-with-external-commonjs-modules) | ||
### Browsers | ||
The most-recent release is persisted in git at `dist/testdouble.js`. You can download it [here](https://raw.githubusercontent.com/testdouble/testdouble.js/master/dist/testdouble.js). The library will set the global `window.testdouble`. | ||
## Creating Test Doubles | ||
### Creating test double functions with `function()` | ||
The easiest way to create a test double function is to make one anonymously: | ||
``` javascript | ||
var td = require('testdouble'); | ||
var myTestDouble = td.function(); | ||
``` | ||
In the above, `myTestDouble` will be able to be stubbed or verified as shown below. | ||
#### Naming your test double | ||
For slightly easier-to-understand error messages (with the trade-off of greater | ||
redundancy in your tests), you can supply a string name to `create` | ||
``` javascript | ||
var myNamedDouble = td.function("#foo"); | ||
``` | ||
All error messages and descriptions provided for the above `myNamedDouble` will | ||
also print the name `#foo`. | ||
### Creating test doubles objects with `object()` | ||
It's very typical that the code under test will depend not only on a single | ||
function, but on an object type that's full of them. | ||
#### Dynamic test double objects | ||
If your tests run under ES2015 or later and [Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) | ||
is available (as of 2015/10/26: only under MS Edge & Firefox) in your JS runtime, | ||
then test double can create a test double object that can set up stubbings or | ||
verify function invocations for any name, completely dynamically! | ||
``` javascript | ||
var cat = td.object('Cat') | ||
td.when(cat.meow()).thenReturn('purr') | ||
cat.meow() // returns 'purr' | ||
cat.literallyAnythingAtAll() // undefined | ||
``` | ||
##### Excluding certain methods from the double | ||
Sometimes, your subject code will check to see if a property is defined, which | ||
may make a bit of code unreachable when a dynamic test double responds to every | ||
single accessed property. | ||
For example, if you have this code: | ||
``` javascript | ||
function leftovers(walrus) { | ||
if(!walrus.eat) { | ||
return 'cheese'; | ||
} | ||
} | ||
``` | ||
You could create a test double walrus that can reach the cheese with this: | ||
``` javascript | ||
walrus = td.object('Walrus', {excludeMethods: ['eat']}); | ||
leftovers(walrus) // 'cheese' | ||
``` | ||
By default, `excludeMethods` is set to `['then']`, so that test libraries like | ||
Mocha don't mistake every test double object for a Promise (which would cause the | ||
test suite to time out) | ||
#### Mirroring an instantiable type | ||
Suppose your subject has a dependency: | ||
``` javascript | ||
function Dog() {}; | ||
Dog.prototype.bark = function(){}; | ||
Dog.prototype.bite = function(){}; | ||
``` | ||
Then you can create a test double of `Dog` with: | ||
``` javascript | ||
var myDogDouble = td.object(Dog) | ||
``` | ||
This will return a plain object with `bark` and `bite` test double functions, | ||
ready to be stubbed or verified and named `"Dog#bark"` and `"Dog#bite"`, | ||
respectively. | ||
## Stub with `when()` | ||
To stub values with testdouble.js, first create one: | ||
``` javascript | ||
var td = require('testdouble'); | ||
myTestDouble = td.function(); | ||
``` | ||
You can stub a no-arg invocation like so: | ||
``` javascript | ||
td.when(myTestDouble()).thenReturn("HEY"); | ||
myTestDouble(); // returns "HEY" | ||
``` | ||
You can stub a specific set of args (performs lodash's `_.isEqual` on each) with: | ||
``` javascript | ||
td.when(myTestDouble('a', 5, {foo: 'bar'})).thenReturn("YES"); | ||
myTestDouble('a', 5, {foo: 'bar'}); // returns "YES" | ||
myTestDouble('a', 5, {foo: 'baz'}); // returns undefined | ||
``` | ||
## Verify with `verify()` | ||
You can verify the behavior of methods with side-effects so long as you promise | ||
to: | ||
* Default to writing code that returns meaningful values and to asserting on those | ||
* Never verify an invocation you've stubbed. If the stubbing is necessary for the | ||
test to pass, then the verification is redundant. | ||
That said, lots of code has side-effects, and to test-drive those interactions, | ||
you can use the `verify` function. | ||
First, create a test double: | ||
``` javascript | ||
var td = require('testdouble'); | ||
var myTestDouble = td.function(); | ||
``` | ||
Now, suppose you've passed this function into your [subject](https://github.com/testdouble/contributing-tests/wiki/Subject) | ||
and you want to verify that it was called with the arguments `("foo", 5)`: | ||
``` javascript | ||
subject.callTheThingThatShouldBeInvokingMyTestDouble() | ||
td.verify(myTestDouble("foo", 5)) | ||
``` | ||
Just invoke the method as you want to see it invoked inside a call to `verify()`. | ||
If the verification succeeded, nothing will happen. If the verification fails, | ||
you'll see an error like this one: | ||
``` | ||
Unsatisfied test double verification. | ||
Wanted: | ||
- called with `("WOAH")`. | ||
But there were no invocations of the test double. | ||
``` | ||
## Argument matchers in `matchers` | ||
The library also supports argument matchers for stubbing and verifying. While, in | ||
many cases it's sufficient to rely on the default deep-equality check, sometimes | ||
a looser specification is necessary (e.g. maybe it only matters that _some_ | ||
number be passed to a method, or maybe we only want to verify _part_ of a large | ||
object was passed to another method). | ||
Some matchers are provided out of the box via the `require('testdouble').matchers` | ||
top-level object. You can alias any or all of these functions to globals if you | ||
prefer the terseness of a DSL-style, or you can use the fully-qualified paths. | ||
You can see the built-in matchers in the [source](https://github.com/testdouble/testdouble.js/blob/master/lib/matchers/index.coffee). | ||
Here's an example usage of the provided `isA()` matcher: | ||
``` javascript | ||
var td = require('testdouble'); | ||
var myTestDouble = td.function(); | ||
when(myTestDouble(td.matchers.isA(String))).thenReturn("YES"); | ||
myTestDouble() // undefined | ||
myTestDouble(5) // undefined | ||
myTestDouble("HI") // returns "YES" | ||
myTestDouble(new String("neato")) // returns "YES" | ||
``` | ||
Matchers can also be used to relax or augment the `verify()` method, like so: | ||
``` javascript | ||
verify(myTestDouble(td.matchers.isA(Date))) | ||
``` | ||
Will throw an error unless something like `myTestDouble(new Date())` was | ||
previously invoked. | ||
### Writing your own matcher | ||
There's nothing magical about matchers. Any object passed into a `when()` or | ||
`verify()` invocation that has a `__matches` function on it and returns truthy | ||
when it matches and falsey when it doesn't can be a matcher. | ||
Here's a naive implementation of `isA` from above (don't use this, as it's | ||
incomplete): | ||
``` javascript | ||
isA = function(type) { | ||
return { | ||
__matches: function(actual) { | ||
return actual instanceof type; | ||
} | ||
}; | ||
``` | ||
This pattern—a function that takes matcher configuration which is then referenced | ||
via lexical scoping in the `__matches` function itself—is very common. | ||
Most matchers other than an `anything()` matcher will need some sort of input to | ||
compare against for the actual argument. | ||
### Using Argument Captors | ||
An argument captor is a object that provides a function called "capture", which | ||
itself is a special type of an argument matcher, which "captures" an | ||
actual value that's passed into a test double by your subject code, such that | ||
your test can access that value. Typically, a test would want to use a captor | ||
when complex assertion logic is necessary or when an anonymous function is | ||
passed into a test double and that function should be put under test directly. | ||
**Beware before using:** if your test double is receiving an argument that can't | ||
be verified with the default equality check or a more straightforward matcher, | ||
ask yourself if the contract between the subject and the test double is as simple | ||
as it should be before trying to salve the "gee, this is hard to test" pain by | ||
using a captor. In my practice, I typically only use captors when I'm testing | ||
legacy code or when exposing an anonymous function via a public API would be | ||
undesirable. **/BEWARE** | ||
Here's an example. Suppose you want to write a test that would specify this bit | ||
of code: | ||
``` | ||
function logInvalidComments(fetcher, logger) { | ||
fetcher('/comments', function(response){ | ||
response.comments.forEach(function(comment) { | ||
if(!comment.valid) { | ||
logger('Hey, '+comment.text+' is invalid'); | ||
} | ||
}); | ||
}); | ||
} | ||
``` | ||
You could use an argument captor to write a sort of two-staged test for both the | ||
top-level function along with its embedded anonymous function. | ||
``` | ||
//Stage 1: Test the outer function | ||
var td = require('testdouble'), | ||
assert = require('assert'), | ||
logger = td.function('logger'), | ||
fetcher = td.function('fetcher'), | ||
captor = td.matchers.captor(); | ||
logInvalidComments(fetcher, logger); | ||
td.verify(fetcher('/comments', captor.capture())); | ||
// Stage 2: Now we test the anonymous function passed to `fetcher` | ||
var response = {comments: [{valid: true}, {valid: false, text: 'PANTS'}]}; | ||
captor.value(response); | ||
assert.ok(td.explain(logger).callCount === 1); | ||
td.verify(logger('Hey, PANTS is invalid')); | ||
``` | ||
This style is definitely verbose, but it's very explicit and entirely | ||
synchronous. Rather than write asynchronous unit tests of asynchronous code, | ||
this pattern enables developers to maintain control over how their code executes | ||
by testing it synchronously. The benefits to this are comprehensability of what | ||
the test does at runtime, easier debugging, and no reliance on a test framework | ||
to provide async support. | ||
## Debug with `explain()` | ||
One shortcoming of lots of JavaScript test double libraries is pretty rotten | ||
introspection and output. While this library is generally pretty minimal, some | ||
data about your test doubles can be gleaned by passing them to a top-level | ||
`explain` function, like so: | ||
``` javascript | ||
var td = require('testdouble'); | ||
var myTestDouble = td.function(); | ||
td.explain(myTestDouble); /* | ||
Returns: | ||
{ | ||
callCount: 0, | ||
calls: [], | ||
description: 'This test double has 0 stubbings and 0 invocations.' | ||
} | ||
*/ | ||
``` | ||
If the test double does have stubbings or invocations, they'll be listed in the | ||
description body for nicer error output. | ||
## Get a clean slate with `reset()` | ||
There are times when you need to re-use the same double over multiple tests. | ||
When that happens, you need to reset the state of the double to avoid polluting | ||
your tests. You can call `reset()` to achieve this: | ||
```javascript | ||
var td = require('testdouble'); | ||
var myTestDouble = td.function(); | ||
// use myTestDouble however you need | ||
td.reset(myTestDouble); // Forget everything you know | ||
``` | ||
## Configuring interactions | ||
You can pass options to `when` and `verify` like so: | ||
``` | ||
when(someTestDouble(), {ignoreExtraArgs: true}).thenReturn('foo'); | ||
verify(someOtherTestDouble(), {ignoreExtraArgs: true}); | ||
``` | ||
Suported options are. Each should only ever be needed sparingly: | ||
* `ignoreExtraArgs` (default: **false**) a stubbing or verification will be satisfied | ||
if the argument positions which are explicitly specified in a `when` or `verify` | ||
call, and if additional arguments are passed by the subject, the interaction will | ||
still be considered satisfied. Use when you don't care about one, some, or any | ||
of the arguments a test double will receive. | ||
* `times` (default: **undefined**) if set for a verification, will fail to verify | ||
unless a satisfactory invocation was made on the test double function `n` times. | ||
If set for a stubbing, will only return the stubbed value `n` times; afterward, | ||
the stubbing will be effectively deactivated. | ||
## Setup notes | ||
The library is not coupled to any test framework, which means it can be used with | ||
jasmine, QUnit, Mocha, or anything else. However, to get the most out of the library, | ||
you may choose to make a few of the top-level functions global in a test helper | ||
(to cut down on repetitive typing). | ||
Perhaps you want to keep everything namespaced under `td` for short: | ||
``` javascript | ||
global.td = require('testdouble'); | ||
``` | ||
Or, you might prefer to plop the methods directly on the global: | ||
``` javascript | ||
global.double = require('testdouble').function; | ||
global.when = require('testdouble').when; | ||
global.verify = require('testdouble').verify; | ||
``` | ||
Organize it however you like, being mindful that sprinkling in globals might save | ||
on per-test setup cost, but at the expense of increased indirection for folks | ||
unfamiliar with the test suite's setup. | ||
## Unfinished business | ||
The rest of the stuff we'd like to do with this is a work-in-progress. See the [issues](https://github.com/testdouble/testdouble.js/issues) for more detail on where we're headed. |
@@ -17,3 +17,3 @@ var pkg = require('./package.json'); | ||
before_tests: "npm run compile:tests && npm run build", | ||
before_tests: "npm run compile", | ||
//might want to add this if you do a lot of file-delete/add churn; faster w/o. | ||
@@ -39,5 +39,5 @@ //after_tests: "npm run clean", | ||
watch_files: [ | ||
"lib/**/*", | ||
"src/**/*", | ||
"test/**/*" | ||
] | ||
}; |
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 too big to display
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Native code
Supply chain riskContains native code (e.g., compiled binaries or shared libraries). Including native code can obscure malicious behavior.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1101652
122
27997
10
84
9
3
+ Addedquibble@0.3.1(transitive)
- Removedcoffee-script@1.12.7(transitive)
- Removedquibble@0.2.1(transitive)
Updatedquibble@^0.3.0