Testing logic stores
NOTE! This section of the guide is still heavily under construction! Please check back in a few days.
Generic test setup
For testing kea we recommend using jest.
Resetting the cache before each test
// logic.test.js
import { resetKeaCache } from 'kea'
beforeEach(() => {
  resetKeaCache()
})
test('starts from a clear state', () => {
  // ...
})
Creating a store for your tests
import { resetKeaCache, keaReducer } from 'kea'
import { keaSaga } from 'kea-saga'
import { createStore, applyMiddleware, combineReducers, compose } from 'redux'
import createSagaMiddleware from 'redux-saga'
function getStore () {
  resetKeaCache()
  const reducers = combineReducers({
    scenes: keaReducer('scenes')
  })
  const sagaMiddleware = createSagaMiddleware()
  const finalCreateStore = compose(
    applyMiddleware(sagaMiddleware)
  )(createStore)
  const store = finalCreateStore(reducers)
  sagaMiddleware.run(keaSaga)
  return store
}
test('do something with the store', () => {
  const store = getStore()
  // ...
})
Testing logic
/* global expect, test */
import { keaReducer } from 'kea'
import PropTypes from 'prop-types'
import getStore from './helpers/get-store'
test('homepage logic has all the right properties', () => {
  const store = getStore()
  // we must require because importing makes
  const logic = require('./logic').default
  expect(logic.path).toEqual(['scenes', 'homepage', 'index'])
  // actions
  expect(Object.keys(logic.actions)).toEqual(['updateName'])
  const { updateName } = logic.actions
  expect(typeof updateName).toBe('function')
  expect(updateName.toString()).toBe('update name (homepage.index)')
  expect(updateName('newname')).toEqual({ payload: { name: 'newname' }, type: updateName.toString() })
  // reducers
  const defaultValues = { name: 'Chirpy' }
  const state = { scenes: { homepage: { index: defaultValues } } }
  expect(Object.keys(logic.reducers).sort()).toEqual(['capitalizedName', 'name'])
  expect(logic.reducers).toHaveProperty('name.reducer')
  expect(logic.reducers).toHaveProperty('name.type', PropTypes.string)
  expect(logic.reducers).toHaveProperty('name.value', 'Chirpy')
  const nameReducer = logic.reducers.name.reducer
  expect(Object.keys(nameReducer)).toEqual([ updateName.toString() ])
  expect(nameReducer[updateName.toString()]).toBeDefined()
  expect(nameReducer[updateName.toString()]('', { name: 'newName' })).toBe('newName')
  expect(logic.reducers).not.toHaveProperty('capitalizedName.reducer')
  expect(logic.reducers).toHaveProperty('capitalizedName.type', PropTypes.string)
  expect(logic.reducers).not.toHaveProperty('capitalizedName.value', 'chirpy')
  // big reducer
  expect(typeof logic.reducer).toBe('function')
  expect(logic.reducer({}, { type: 'random action' })).toEqual(defaultValues)
  expect(logic.reducer({ name: 'something' }, { type: 'random action' })).toEqual({ name: 'something' })
  expect(logic.reducer({ name: 'something' }, updateName('newName'))).toEqual({ name: 'newName' })
  // selectors
  expect(Object.keys(logic.selectors).sort()).toEqual(['capitalizedName', 'name', 'root'])
  expect(logic.selectors.name(state)).toEqual('Chirpy')
  expect(logic.selectors.capitalizedName(state)).toEqual('Chirpy')
  // root selector
  expect(logic.selector(state)).toEqual(defaultValues)
  expect(logic.selectors.root(state)).toEqual(defaultValues)
})
Testing components with enzyme
/* global test, expect */
import './helper/jsdom'
import React, { Component } from 'react'
import { kea } from 'kea'
import PropTypes from 'prop-types'
import { mount } from 'enzyme'
import { Provider } from 'react-redux'
import getStore from './helper/get-store'
class SampleComponent extends Component {
  render () {
    const { id, name, capitalizedName } = this.props
    const { updateName } = this.actions
    return (
      <div>
        <div className='id'>{id}</div>
        <div className='name'>{name}</div>
        <div className='capitalizedName'>{capitalizedName}</div>
        <div className='updateName' onClick={updateName}>updateName</div>
      </div>
    )
  }
}
test('connects to react components', () => {
  const store = getStore()
  const dynamicLogic = kea({
    key: (props) => props.id,
    path: (key) => ['scenes', 'something', key],
    actions: ({ constants }) => ({
      updateName: name => ({ name })
    }),
    reducers: ({ actions, constants }) => ({
      name: ['chirpy', PropTypes.string, {
        [actions.updateName]: (state, payload) => payload.name + payload.key
      }]
    }),
    selectors: ({ constants, selectors }) => ({
      capitalizedName: [
        () => [selectors.name],
        (name) => {
          return name.trim().split(' ').map(k => `${k.charAt(0).toUpperCase()}${k.slice(1).toLowerCase()}`).join(' ')
        },
        PropTypes.string
      ]
    })
  })
  const ConnectedComponent = dynamicLogic(SampleComponent)
  const wrapper = mount(
    <Provider store={store}>
      <ConnectedComponent id={12} />
    </Provider>
  )
  expect(wrapper.find('.id').text()).toEqual('12')
  expect(wrapper.find('.name').text()).toEqual('chirpy')
  expect(wrapper.find('.capitalizedName').text()).toEqual('Chirpy')
  // for now we must dispatch a discard action to regenerate the store
  store.dispatch({ type: 'discard' })
  expect(store.getState()).toEqual({ scenes: { something: { 12: { name: 'chirpy' } } } })
  const sampleComponent = wrapper.find('SampleComponent').node
  expect(sampleComponent.actions).toBeDefined()
  expect(Object.keys(sampleComponent.actions)).toEqual(['updateName'])
  const { updateName } = sampleComponent.actions
  updateName('somename')
  expect(store.getState()).toEqual({ scenes: { something: { 12: { name: 'somename12' } } } })
  wrapper.render()
  expect(wrapper.find('.id').text()).toEqual('12')
  expect(wrapper.find('.name').text()).toEqual('somename12')
  expect(wrapper.find('.capitalizedName').text()).toEqual('Somename12')
})
Other resources
Check out the source documentation on testing redux, selectors and sagas.