fzkes v0.15.0
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][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.