Test stack in CM project

MSD.js

and what we learned

Comparator modelling in July 2016

  • React.js + alt.js
  • No single unit test
  • Antipatters (this.props.items.push(...))
  • Requests being made inside stores
  • Constantly failing robot tests as a result
  • Unstable project after just 1/2 year of development

Building unit tests - questions

  • What to test? Where to start?
  • Stack ready to use vs. custom stack on top of current one?

Our  test stack

  • Mocha

    • our choice for test framework
  • Sinon

    • library for mocking API requests, action calls, time etc.
  • Chai.js

    • assertion library with fabulous grammar
  • Enzyme

    • Utility to help with React.js components
  • Karma

    • test runner because several components are depending heavily on browser environment

Sinon - what we learned

  • version 1.x doesn't work well with webpack

import sinon from 'sinon'

// undefined' is not an object (evaluating 'modules[moduleId].call')
// webpack configuration

resolve: {
    alias: {
      // it's necessary to import dist instead of sinon module 
      // exported by default and this hides this ugly fact from us
      sinon: 'sinon/pkg/sinon.js'
    }
}

Sinon

// some tested code
callAsync = (cb) => {
  doStuff();

  setTimeout(cb, 300);
};


// test
const fakedClocks = sinon.useFakeTimers();
const spy = sinon.spy();

callAsync(spy);
fakedClocks.tick(310);
expect(spy).to.have.been.calledOnce;

fakedClocks.restore();

Chai.js

  • Really rich grammar + lot of useful plugins:
    
    it('shows table header', function () {
      const headerContent = tableHeaderWrapper.find('.header-content');

      expect(headerContent).to.be.present();
      expect(headerContent).to.contain.text('Author name');
    });

    it('does not show filter popup by default', function () {
      expect(tableHeaderWrapper.find('FilterContainer')).to.not.be.present();
    });

Enzyme

render, shallow, mount - what to use?

  • render

    • when you are interested only in html

  • shallow

    • interactions - clicks, mouse events etc.
  • mount

    • when you need DOM or lifecycle methods

Enzyme

Component displayName is important


const Loading = (props) => {
  const msg = props.message || 'Loading...';

  return (
    <div className='loading__wrap'>
      <div className='loading__inner'>
        <img
          src={loading}
          className='loading__spinner'
          alt='loading'
        />
        <p>{msg}</p>
      </div>
    </div>
  );
};

// won't work without it:
// expect(parentComponent.find('Loading')).to.be.present();
Loading.displayName = 'Loading';

export default Loading;

What we learned?

  • Building stack is harsh
  • There are ready-to-use solutions
  • Don't plan unit testing, just start

test-stack-msd

By Jaroslav Kubíček

test-stack-msd

  • 236