top of file:
const _getDependencies = () => ({
LION_NAME: 'simba',
roar: _roar,
})
later in file:
export const hakunaMutata = (_ = _getDependencies()) => {
console.log(`woah ${_.LION_NAME} such gr8 sing`)
_.roar()
}
unit test:
let dependencies
beforEach(() => {
dependencies = {
// lots of things for all functions in file under test
}
})
it('`hakunaMutata` calls `roar`', () => {
// set up mock function to assert against
const roar = jest.fn()
// bake mock into args
dependencies = {
...dependencies,
roar,
}
// invoke function with new dependencies
hakunaMutata(dependencies)
// assert
expect(roar).toHaveBeenCalled()
})
Pros:
- pure functions that are easily tested
Cons:
- tests for files with many dependencies in initial
_getDependencies
object can be hard to maintain - obscures exactly what dependencies are used in each function call
- hard to tell if we have any superfluous dependencies in
_getDependencies
- no singular
_getDependencies
at top of file that is used by all functions; instead, we specify_getDependencies
per function and only pass in exactly what dependencies each function needs:
function _getWisdom(lionName, _ = _getWisdom_deps()) {
console.log(`${lionName} is going to get some really good wisdom!`)
_.rafiki(lionName)
}
it('`_getWisdom` calls `rafiki`', () => {
const rafiki = jest.fn()
const deps = {
rafiki,
}
const lionName = 'simba'
_getWisdom(lionName, deps)
expect(rafiki).toHaveBeenCalledWith(lionName)
})
Benefits:
- easy to see what dependencies are used per function
- more straightforward to write/update tests
- also we could use the shortened
deps
instead ofdependencies
Cons:
- test-specific-code is peppered throughout files instead of one
_getDependencies
block at top - diverging patterns in code make onboarding new devs harder