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.
Let's look at the basic example from the Domain page:
actions.jsconst 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.
__test__/actions.js// stub Date.nowDate.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')})
And we can even execute it:
reducer.jsconst 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)
​
__test__/reducer.jsconst 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()})