Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/fizker/fzkes

A faking library
https://github.com/fizker/fzkes

Last synced: 4 days ago
JSON representation

A faking library

Awesome Lists containing this project

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:

1. Restoring a single fake: `fake.restore()`
2. 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 as
`function(error, returnValue)`, where `error` is whatever the callback
threw, and `returnValue` is what the callback returned. If `async` is
false, the fake will return whatever the `notify` 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 of `notify`, 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 ] })

[mocha]: http://mochajs.org/

### 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](http://chaijs.com)

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.