I'm trying to test flux stores. I use ReduceStore
from flux/utils
, jest
v0.7.0. When I dispatch an action with the mocked dispatcher I get an error:
Error: Invariant Violation: Store.__emitChange(): Must be invoked while dispatching.
If I use this store in a real browser, it works as expected.
Here is my store:
import {ReduceStore} from 'flux/utils';
import Immutable from 'immutable';
import Dispatcher from '../dispatcher';
import actionConstants from '../action-constants';
class EntryStore extends ReduceStore {
getInitialState() {
return Immutable.List();
}
reduce(state, action) {
switch (action.type) {
case actionConstants.ENTRIES_REQUEST_SUCCESS:
return state.merge(action.payload.entries);
default:
return state;
}
}
}
const instance = new EntryStore(Dispatcher);
export default instance;
And, here is a test file:
jest.autoMockOff();
jest.mock('../../dispatcher');
const actionConstants = require('../../action-constants');
describe('EntryStore', function() {
let Dispatcher, EntryStore, callback;
// mock entries
entries = [1, 2, 3];
// mock actions
const Immutable = require('immutable');
const entriesRequestSuccess = {
type: actionConstants.ENTRIES_REQUEST_SUCCESS,
payload: {
entries: Immutable.List(entries)
}
}
beforeEach(function() {
Dispatcher = require('../../dispatcher');
EntryStore = require('../entry-store');
callback = Dispatcher.register.mock.calls[0][0];
});
it('should update entries when entries request succeed', function(done) {
callback(entriesRequestSuccess);
let all = EntryStore.getState();
expect(all.size).toBe(3);
});
});
[EDIT]
Ok the first solution I proposed here is actually quite wrong. The bad thing about it is that when really use dispatch
well it send the action to all the tested stores. So you end up having all your stores called while only testing a single one. That's a side effect you might not want.
So I came up with a far better and simplier solution. The idea : simply mock the isDispatching
method of the dispatcher
to always return true
.
This way your stores won't complain when calling the emitChange
method. For example with sinon.js
sinon.stub(dispatcher, "isDispatching", function () { return true; });
It looks a lot better isn't it ? :)
---------------------PREVIOUS SOLUTION------------------------
THIS IS NOT A VIABLE SOLUTION!!
Well looking at this piece of code (https://github.com/facebook/flux/blob/master/src/stores/FluxStore.js#L131) and because state.merge()
method calls the __emitChange()
method, I guess we cannot test our stores with a mocked dispatcher.
So I ended up not mocking my dispatcher and simply call the dispatch
method in my tests.
var dispatcher = require("path/to/appDispatcher")
UserStore = require("path/to/userStore");
var userStore = new UserStore(dispatcher);
it("should load user datas", function () {
var user = {
firstname: "Felix",
lastname: "Anon"
};
//actually dispatch the event
dispatcher.dispatch(actions.RECEIVE_USER, { user: user });
//test the state of the store
expect(userStore.get()).to.be(user);
});
I guess this is an acceptable solution even if it is not the one suggested in the official doc.