reactjsecmascript-6reactjs-fluxjestjs

Testing React Flux Store with Jest in ES6


I'm trying to test my React store using Jest and ES6 classes. I was wondering how to "reset" the tested store or getting a fresh instance before every single test.

My store contains:

import BaseStore from './BaseStore';
import { MIDI_PLUGIN_LOADED } from '../constants/MidiConstants';


class MidiStore extends BaseStore {

  constructor() {
    super();
    this.subscribe(() => this._registerToActions.bind(this));
    this._midiPlayer = null;
  }

  _registerToActions(action) {
    switch (action.actionType) {
      case MIDI_PLUGIN_LOADED:
        this._midiPlayer = action.player;
        this.emitChange();
        break;
    }
  }

  get midiPlayer() {
    return this._midiPlayer;
  }
}

export default new MidiStore();

My Jest test code:

import { MIDI_PLUGIN_LOADED } from '../../constants/MidiConstants';
import AppDispatcher from '../../dispatchers/AppDispatcher';
import MidiStore from '../MidiStore';

describe('MidiStore', () => {

  var actionMidiPluginLoaded = {
    actionType: MIDI_PLUGIN_LOADED,
    player: true
  };

  it('stores global midi plugin', () => {
    AppDispatcher.dispatch(actionMidiPluginLoaded);
    let {
      midiPlayer
    } = MidiStore;

    expect(midiPlayer).toBe(true);
  });

  // fails cause midiPlayer = true
  it('should initialize with no player', () => {
    let {
      midiPlayer
    } = MidiStore;

    expect(midiPlayer).toBeNull();
  });

});

The problem is that the second "it"-statement fails, because the MidiStore isn't resetted after the first run.

I know that switching the two "it"-statements would pass both tests, but that isn't a real solution.

In ES5 Jest it was possible to call var MidiStore = require('../MidiStore); in a beforeEach to get a new instance on every run. How can i accomplish this with ES6?


Solution

  • I managed to get around the problem myself. By using "old" require in jests beforeEach callback it is possible to grab a new instance for each test function.

    import { MIDI_PLUGIN_LOADED } from '../../constants/MidiConstants';
    
    jest.mock('../../dispatchers/AppDispatcher');
    
    describe('MidiStore', () => {
    
      var AppDispatcher;
      var MidiStore;
      var callback;
    
      var actionMidiPluginLoaded = {
        actionType: MIDI_PLUGIN_LOADED,
        player: true
      };
    
      beforeEach(() => {
        jest.resetModules();
        AppDispatcher = require('../../dispatchers/AppDispatcher').default;
        MidiStore = require('../MidiStore').default;
        callback = AppDispatcher.register.mock.calls[0][0];
      });
    
      it('registers a callback with the dispatcher', () => {
        expect(AppDispatcher.register.mock.calls.length).toBe(1);
      });
    
      it('stores global midi plugin', () => {
        callback(actionMidiPluginLoaded);
        expect(MidiStore.midiPlayer).toBe(true);
      });
    
      it('should initialize with no player', () => {
        expect(MidiStore.midiPlayer).toBeNull();
      });
    
    });
    

    In the beforeEach call i reset modules with jest.resetModules(); and grab new instances of dispatcher, store and registered callback. The registered callback is retrieved from the dispatcher, which is now mocked by jest. For implementing mock functions (in other tests), i refered to https://facebook.github.io/jest/docs/api.html#mockfn-mockimplementation-fn