Because your domain is described a series of pure functions, testing becomes ultra-simple: given some input, expect a certain output. No mocking calls to external services or a database.
Copy const actions = {
addTodo : (state , payload) => {
if ( ! payload .title) throw new Error ( 'titleMissing' )
return [{
type : 'TodoAdded' ,
title : payload .title ,
at : Date .now () ,
}]
}
}
module . exports = actions
Now let's write a test for this.
Copy // stub Date.now
Date . now = () => 123
const test = require ( 'tape' )
const actions = require ( '../actions' )
test ( 'addTodo' , assert => {
assert .deepEquals (
actions .addTodo ({} , { title : 'foobar' }) ,
[{
type : 'TodoAdded' ,
title : 'foobar' ,
at : 123
}] ,
'generates a TodoAdded event'
)
assert .throws (
() => actions .addTodo ({} , { foo : 'bar' }) ,
/titleMissing/ ,
'throws if title is missing'
)
})
Copy const initialState = {
todos : []
}
const reducer = (state , event) => {
switch ( event .type) {
case 'TodoAdded' :
return {
todos : [
... state .todos ,
{ title : event .title } ,
]
}
default :
return state
}
}
module . exports = (events , state = initialState) => events .reduce (reducer , state)
Copy const test = require ( 'tape' )
const reducer = require ( '../reducer' )
test ( 'todoReducer' , assert => {
const events = [
{ type : 'TodoAdded' , title : 'get milk' } ,
{ type : 'TodoAdded' , title : 'borrow sugar' } ,
{ type : 'Invalid' , title : 'ignore this' } ,
]
const expected = {
todos : [
{ title : 'get milk' } ,
{ title : 'borrow sugar' } ,
]
}
const result = reducer (events)
assert .deepEquals (result , expected , 'reduces events' )
assert .end ()
})