Added basic spy() and stub() functionality

This commit is contained in:
Jonatan Nilsson 2021-10-09 23:02:51 +00:00
parent 084b751b5b
commit 5415f2ba95
3 changed files with 240 additions and 0 deletions

View file

@ -1,7 +1,10 @@
import Eltro from './lib/eltro.mjs' import Eltro from './lib/eltro.mjs'
import assert from './lib/assert.mjs' import assert from './lib/assert.mjs'
import { spy, stub } from './lib/sinon.mjs'
export { export {
Eltro, Eltro,
assert, assert,
spy,
stub,
} }

55
lib/sinon.mjs Normal file
View file

@ -0,0 +1,55 @@
const indexMap = [
'firstCall',
'secondCall',
'thirdCall',
]
export function spy() {
return stub()
}
export function stub(returnFunc = null) {
if (returnFunc && typeof(returnFunc) !== 'function') {
throw new Error('stub() was called with non-function argument')
}
let returner = returnFunc ? returnFunc : null
let calls = []
let func = function(...args) {
func.called = true
calls.push(args)
if (func.callCount < indexMap.length) {
func[indexMap[func.callCount]] = args
}
func.callCount++
if (returner) {
return returner(...args)
}
}
func.called = false
func.callCount = 0
func.onCall = function(i) {
return calls[i]
}
func.returns = function(data) {
returner = function() { return data }
}
func.returnWith = function(returnFunc) {
if (typeof(returnFunc) !== 'function') {
throw new Error('stub() was called with non-function argument')
}
returner = returnFunc
}
func.throws = function(data) {
returner = function() { throw data }
}
func.resolves = function(data) {
returner = function() { return Promise.resolve(data) }
}
func.rejects = function(data) {
returner = function() { return Promise.reject(data) }
}
for (let i = 0; i < indexMap.length; i++) {
func[indexMap[i]] = null
}
return func
}

182
test/sinon.test.mjs Normal file
View file

@ -0,0 +1,182 @@
import assert from '../lib/assert.mjs'
import t from '../lib/eltro.mjs'
import { spy, stub } from '../index.mjs'
[spy, stub].forEach(function(tester, i) {
t.describe(`#${i === 0 ? 'spy' : 'stub'}()`, function() {
t.test('should keep track of call count', function() {
let spyer = tester()
assert.strictEqual(spyer.callCount, 0)
assert.notOk(spyer.called)
assert.strictEqual(spyer.firstCall, null)
assert.strictEqual(spyer.secondCall, null)
assert.strictEqual(spyer.thirdCall, null)
spyer()
assert.strictEqual(spyer.callCount, 1)
assert.ok(spyer.called)
assert.deepStrictEqual(spyer.firstCall, [])
assert.strictEqual(spyer.secondCall, null)
assert.strictEqual(spyer.thirdCall, null)
spyer()
assert.strictEqual(spyer.callCount, 2)
assert.ok(spyer.called)
assert.deepStrictEqual(spyer.firstCall, [])
assert.deepStrictEqual(spyer.secondCall, [])
assert.strictEqual(spyer.thirdCall, null)
spyer()
assert.strictEqual(spyer.callCount, 3)
assert.ok(spyer.called)
assert.deepStrictEqual(spyer.firstCall, [])
assert.deepStrictEqual(spyer.secondCall, [])
assert.deepStrictEqual(spyer.thirdCall, [])
spyer()
assert.strictEqual(spyer.callCount, 4)
})
t.test('should keep track of arguments used to call', function() {
const assertFirstArgs = 'asdf'
const assertSecondArgs = { a: 1 }
const assertThirdArgs = [{ b: 1 }, { c: 2 }]
let spyer = tester()
assert.notOk(spyer.called)
spyer(assertFirstArgs)
spyer(assertSecondArgs)
spyer(assertThirdArgs[0], assertThirdArgs[1])
assert.strictEqual(spyer.callCount, 3)
assert.strictEqual(spyer.onCall(0)[0], assertFirstArgs)
assert.strictEqual(spyer.onCall(1)[0], assertSecondArgs)
assert.strictEqual(spyer.onCall(2)[0], assertThirdArgs[0])
assert.strictEqual(spyer.onCall(2)[1], assertThirdArgs[1])
assert.strictEqual(spyer.firstCall[0], assertFirstArgs)
assert.strictEqual(spyer.secondCall[0], assertSecondArgs)
assert.strictEqual(spyer.thirdCall[0], assertThirdArgs[0])
assert.strictEqual(spyer.thirdCall[1], assertThirdArgs[1])
})
})
})
t.describe('#stub()', function() {
t.test('should support returns', function() {
const assertInput = { a : 1 }
const assertReturns = { b: 3 }
let s = stub()
assert.strictEqual(s(), undefined)
s.returns(assertReturns)
assert.strictEqual(s(assertInput), assertReturns)
assert.ok(s.called)
assert.strictEqual(s.callCount, 2)
assert.strictEqual(s.secondCall[0], assertInput)
})
t.test('should support throws', function() {
const assertInput = { a : 1 }
const assertThrows = new Error('testety test')
let s = stub()
assert.strictEqual(s(), undefined)
s.throws(assertThrows)
assert.throws(function() {
s(assertInput)
}, assertThrows)
assert.ok(s.called)
assert.strictEqual(s.callCount, 2)
assert.strictEqual(s.secondCall[0], assertInput)
})
t.test('should support resolves', async function() {
const assertInput = { a : 1 }
const assertReturns = { b: 3 }
let s = stub()
assert.strictEqual(s(), undefined)
s.resolves(assertReturns)
let promiser = s(assertInput)
assert.ok(promiser.then)
assert.strictEqual(typeof(promiser.then), 'function')
let output = await promiser
assert.strictEqual(output, assertReturns)
})
t.test('should support rejects', async function() {
const assertInput = { a : 1 }
const assertReturns = { b: 3 }
let s = stub()
assert.strictEqual(s(), undefined)
s.rejects(assertReturns)
let promiser = s(assertInput)
assert.ok(promiser.then)
assert.strictEqual(typeof(promiser.then), 'function')
let output = await assert.isRejected(promiser)
assert.strictEqual(output, assertReturns)
})
t.test('should support custom function overwrite', async function() {
const assertInput = { a : 1 }
const assertReturns = { b: 3 }
let ourCallCount = 50
let s = stub(function(input) {
ourCallCount++
assert.strictEqual(input, assertInput)
return assertReturns
})
assert.strictEqual(s(assertInput), assertReturns)
assert.ok(s.called)
assert.strictEqual(s.callCount, 1)
assert.strictEqual(ourCallCount, 51)
assert.strictEqual(s.firstCall[0], assertInput)
assert.throws(function() {
s(null)
})
})
t.test('should support custom function overwrite after the fact', async function() {
const assertInput = { a : 1 }
const assertReturns = { b: 3 }
let ourCallCount = 0
let s = stub()
assert.strictEqual(s(null), undefined)
s.returnWith(function(input) {
ourCallCount++
assert.strictEqual(input, assertInput)
return assertReturns
})
assert.strictEqual(s(assertInput), assertReturns)
assert.ok(s.called)
assert.strictEqual(s.callCount, 2)
assert.strictEqual(ourCallCount, 1)
assert.strictEqual(s.secondCall[0], assertInput)
assert.throws(function() {
s(null)
})
})
t.test('should throw if parameter is something else than a function', function() {
assert.ok(stub(null))
assert.ok(stub(0))
assert.ok(stub(''))
assert.strictEqual(stub(null)(), undefined)
assert.strictEqual(stub(0)(), undefined)
assert.strictEqual(stub('')(), undefined)
assert.throws(function() { stub([]) })
assert.throws(function() { stub({}) })
assert.throws(function() { stub(123) })
assert.throws(function() { stub('asdf') })
})
t.test('should throw if returnWith is called with something else than a function', function() {
assert.throws(function() { stub().returnWith(0) })
assert.throws(function() { stub().returnWith('') })
assert.throws(function() { stub().returnWith(null) })
assert.throws(function() { stub().returnWith([]) })
assert.throws(function() { stub().returnWith({}) })
assert.throws(function() { stub().returnWith(123) })
assert.throws(function() { stub().returnWith('asdf') })
})
})