fzkes
v0.15.0
Published
A faking library
Downloads
24
Maintainers
Readme
fzkes
A faking library
I must immediately apologize for the name; it is a horrible pun combining fzk
(a shortening of the name of my company) and fakes
. But it does have a nice ring to it,
and I want it to be short and unique. So there it is.
How to use
Creating fakes
var fzkes = require('fzkes')
// Creating a stand-alone fake, great for use as a callback
var fake = fzkes.fake()
// For overriding an original function, great for restoring later
var fs = require('fs')
fzkes.fake(fs, 'readFile')
// For faking all functions on an object
fzkes.fakeAll(fs)
fakeAll
The fzkes.fakeAll()
function can take an options-dictionary, allowing for
overriding the default action. The options are:
callsOriginal
: The default.fake.callsOriginal()
is evoked on all fakes.none
: No action is taken on the fakes.throws
: A'Fake not overridden'
exception will be evoked on all fakes.
All fakes can of course be overridden later on, the default is just what should happen out-of-the-box.
Resetting fakes
Resetting a fake (fake.reset()
) will set its internal state to what it was
when it was new.
It is possible to reset all fakes by calling fzkes.reset()
or scope.reset()
.
Restoring original functions
There are three ways to easily doing this, depending on the scope:
- Restoring a single fake:
fake.restore()
- Restoring all fakes across the board:
fzkes.restore()
The last is a bit more tricky; A sub-scope can be creating by calling
fzkes.scope()
. The scope have all methods (except chai
) that the original
fzkes
object have, except the restore()
function on a scope only affects
fakes created within that scope.
Injecting data
fake.returns('abc')
fake.throws(new Error('some error'))
fake.calls(function() { console.log('was called') })
// If it replaced a function on an object:
fake.callsOriginal()
// Conditional injects
fake.withArgs(1,2).returns(3)
fake.withArgs(1,2).callsOriginal()
Each action (returns
, throws
, calls
, etc) returns the fake, to make it
easier to assign to return values and the like:
fake.returns({
a: fzkes.fake('a').returns(3),
b: fzkes.fake('b').throws()
})
It can also chain the withArgs() automatically:
var fake = fzkes.fake('name')
.returns(1)
.withArgs(1,2).returns(3)
.withArgs(1,3).returns(4)
.withArgs(1,4).throws(new Error('some error'))
fake(1,1) // => 1
fake(1,2) // => 3
fake(1,3) // => 4
fake(1,4) // => throws
Advanced withArgs
There is a more advanced form of withArgs
called withComplexArgs
.
It allows for skipping arguments entirely, as well as defining regular
expressions to validate against strings.
var fake = fzkes.fake('name')
.returns(1)
.withComplexArgs(null, { value: 2 }).returns(2)
.withComplexArgs({ regex: /ab?c/ }).returns(3)
fake(1, 1) // => 1
fake(1, 2) // => 2
fake('a', 2) // => 2
fake('abc') // => 3
fake('ac') // => 3
Calling callbacks
There is a built-in helper for calling callbacks: callsArg
. If the callback
throws an exception, the fake with throw it as well.
It can take the following options:
notify
: A function to notify whenever the fake is called. It is called asfunction(error, returnValue)
, whereerror
is whatever the callback threw, andreturnValue
is what the callback returned. Ifasync
is false, the fake will return whatever thenotify
function returns.This option is also perfect for mocha style async handlers.
returns
: A value to return whenever the fake is called. This takes presedence over the return-value ofnotify
, but exceptions still triumph.now
: A flag that determines if the action should occur for future calls or for the first unhandled call. This will throw if the fake have no unhandled calls.async
: A flag determining if the callback should be called immediately or in the next tick (which would simulate an async call).arg
: The argument to call. This can be the parameter index (0-n),'first'
or'last'
. It defaults to'last'
.arguments
: An array of the arguments to pass to the callback.
Examples follow:
// Default is calling the last function found, node-style
fake.callsArg()
// It can be controlled
fake.callsArg({ arg: 'first' })
// 0-indexed argument list
fake.callsArg({ arg: 1 })
// It defaults to calling the callback immediately, but this can be changed
fake.callsArg({ async: true })
// Default is no parameters to the callback, but these can be controlled
fake.callsArg({ arguments: [ 1, 2 ] })
Emulating calls after they have been called
Sometimes, it is not feasible to prepare the fake properly; in these cases, emulating the call after the fact makes the code much better.
fzkes
supports this as an option for the fake.calls()
, fake.callsArg()
and
fake.callsOriginal()
functions.
The code would look as the following:
fake(1,2,3)
fake.calls(fn, { now: true })
fake.callsOriginal({ now: true })
fake.callsArg({ now: true })
It works with all other options on the fake.callsArg()
call.
It forwards the next unhandled call as it appeared on the fake
, and throws an
exception if there are no unhandled calls:
fake(1,2,3)
// Goes through
fake.callsOriginal({ now: true })
try {
fake.calls(fn, { now: true })
} catch(e) {
// e.message would say that fake had no unhandled calls.
}
If any of the functions was set up in advance, calls are not considered
unhandled, and any call with { now: true }
will throw an exception.
To begin building unhandled calls, make a fake.calls(null)
invocation.
fake.callsOriginal()
// this call is handled immediately
fake()
expect(function() {
fake.callsOriginal({ now: true })
}).to.throw()
// resetting the expectations
fake.calls(null)
// it now works again
fake()
fake.callsOriginal({ now: true })
Asserting
fake.wasCalled()
fake.wasCalledWith(1, 2, 3)
fake.wasCalledWithExactly(1, 2, 3)
fake.callCount == 2
Using with chai
chai.use(fzkes)
fake.should.have.been.called // at least once
fake.should.have.been.called(2) // precisely 2 times
fake.should.have.been.calledWith(1,2,3)
Running in the browser
Use a tool like browserify or webpack to generate a browser-compatible version of the tests. Then simply follow the guide above for setting it up and interacting with it.
NOTE: Pre v0.15, the package built a browser-version always. This task have now been delegated to the consumer of the library.