Redux : testing a simple component

25 Feb 2016

In my last post on redux : using with vanilla js we created a simple counter. In this post let’s see how we can test the component as well as redux actions. For testing I will be using mocha testing framework and for assertion expect.js.

For my last post the counter module will look like

import {createStore} from 'redux';

// reducer 
function counter(state=0, action) {
  console.log('counter', action)
  switch(action.type) {
    case 'INCREMENT':
      return state + 1;
    case 'DECREMENT':
      return state - 1;
    default:
      return state;
  }
}

//actions
const increment = () => {
  return {
    type: 'INCREMENT'
  };
};

const decrement = () => {
  return {
    type: 'DECREMENT'
  };
};

//create store
const store = createStore(counter);

class Counter {
  constructor(options) {
    this.$el = options.el;
    this.store = options.store;
    store.subscribe(this.update.bind(this));
    this.$el.querySelector('#inc')
      .addEventListener('click', this.inc.bind(this));
    this.$el.querySelector('#dec')
      .addEventListener('click', this.dec.bind(this));
  }

  inc() {
    this.store.dispatch(increment())
  }

  dec() {
    this.store.dispatch(decrement())
  }

  update() { 
    console.log(store.getState());
    this.$el
      .querySelector('#text')
      .innerHTML = store.getState();
  }

  render() {
    this.update();
  }
}

For testing purpose, I will be adding a new dispatch type called “RESET” which will reset the state to zero.

// reducer 
function counter(state=0, action) {
  switch(action.type) {
    case 'INCREMENT':
      return state + 1;
    case 'DECREMENT':
      return state - 1;
+    case 'RESET':
+      return 0;
    default:
      return state;
  }
}

Testing can be done in two parts, First unit test the actions then the Counter module. In initial setup, we setup for bdd and will create a new store for test

mocha.ui('bdd');

const testStore = createStore(counter);

// our tests

mocha.run();

Testing actions

While testing action we dispatch the events and see the state is changed accordingly. For this before running each test we need to reset the state to zero to make sure other test cases won’t effect one another’s results.

So in beforeEach callback we dispatch the reset event.

describe('actions', () => {
  beforeEach(() => {
    testStore.dispatch({type: 'RESET'});
  });

  // test for actions
});

Now we can write tests for the actions described.

// code for the component from first snippet

mocha.ui('bdd');

const testStore = createStore(counter);

describe('actions', () => {
  beforeEach(() => {
    testStore.dispatch({type: 'RESET'});
  });

  it('inc', () => {
    testStore.dispatch(increment());
    expect(testStore.getState()).toEqual(1);
  });

  it('dec', () => {
    testStore.dispatch(decrement());
    expect(testStore.getState()).toEqual(-1);
  })
});

mocha.run()

So we tested the actions separately, now we can test the actual Counter component.

Testing component

For testing we Counter component we need to create the Counter object. For this I added a new div#test-counter to my HTML and we instantiate the Counter before the tests.

const el = d.getElementById('test-counter');
const c = new Counter({
  el,
  store: testStore
});

Now we can use the variable c in the following tests and we will RESET the store in beforeEach as we did while testing actions. Now for testing we can call c.inc() and c.dec() and check whether the states are changed.

let c;
describe('Counter', () => {
  beforeEach(() => {
    testStore.dispatch({type: 'RESET'});
  });

  it('inc', () => {
    c.inc();
    expect(testStore.getState()).toEqual(1);
  });

  it('dec', () => {
    c.dec();
    expect(testStore.getState()).toEqual(-1);
  });
});

Here is full version of redux and component tests in jsbin.

JS Bin on jsbin.com

If you particularly enjoy my work, I appreciate donations given with Gittip.