Skip to content

Instantly share code, notes, and snippets.

@mridgway
Last active February 22, 2016 22:57
Show Gist options
  • Save mridgway/4d66eb1d3277d8b442f2 to your computer and use it in GitHub Desktop.
Save mridgway/4d66eb1d3277d8b442f2 to your computer and use it in GitHub Desktop.
Upcoming changes in MockActionContext that may affect your tests

Currently ([email protected]) the MockActionContext executes actions in a synchronous manner, which differs from how the actions are executed with the real Fluxible action context. We have found that this introduces inconsistencies when testing nested actions (actions that call other actions) since the callbacks for the nested action will not be called in the correct order or at all. This means that some tests were not even executing the callbacks that contained the test assertions.

To fix this, we are unifying the MockActionContext and ActionContext implementation so that they are identical. This guarantees that the tests will be asserting against the real executeAction implementation.

While the changes should not impact most users, there are some cases where a unit test could be relying on the execution order of parallel actions which could be broken. I have tested against several of the largest codebases inside Yahoo to see how large the impact would be and found that there were very few cases where the tests were broken.

See the example-test.js file. There are two issues here:

  1. The assumption that executeAction is synchronous (it is ALWAYS async). To fix this, you should always use executeAction's callback and put assertions inside the callback.

  2. The assumption that the dispatch order will done completely synchronously. When the application is running (and with the MockActionContext fix), the actual order will be more like:

        'BAR_START',
        'BAZ_START',
        'SOME_OTHER_ACTION',
        'SOME_OTHER_ACTION',
        'BAR_END',
        'BAZ_END'

With that said, you probably shouldn't be testing this way, since it relies on the internals of Fluxible to determine the order of the dispatches. Instead, it is better to use your stores or mock stores to ensure that they are receiving the dispatches and payloads that they expect. If the store relies on a certain order, you can check that the pre-requisite data has already been received in the store.

To prevent a breakage with your tests when we release this fix, we recommend testing your suites by installing a test version of Fluxible (npm i fluxible@test) and updating your tests to work with both the current version and the test version. This will ensure that your tests are robust to handle the internal changes of Fluxible.

var expect = require('chai').expect;
var async = require('async');
var createMockActionContext = require('fluxible/utils/createMockActionContext');
var fooAction = require('example.js');
var FooStore = require('FooStore.js');
it('should dispatch bar and baz', function (done) {
var mockActionContext = createMockActionContext({
stores: [FooStore]
});
mockActionContext.executeAction(fooAction, {}, function () {
var fooStore = mockActionContext.getStore(FooStore);
expect(fooStore.hasReceivedBar()).to.be.true;
expect(fooStore.hasReceivedBaz()).to.be.true;
done();
});
});
var expect = require('chai').expect;
var async = require('async');
var createMockActionContext = require('fluxible/utils/createMockActionContext');
var fooAction = require('example.js');
it('should dispatch bar and baz', function (done) {
var mockActionContext = createMockActionContext();
mockActionContext.executeAction(fooAction, {});
var dispatchNames = mockActionContext.dispatchCalls.map(function (call) {
return call.name;
});
expect(dispatchNames).to.deep.equal([
'BAR_START',
'SOME_OTHER_ACTION',
'BAR_END',
'BAZ_START',
'SOME_OTHER_ACTION',
'BAZ_END'
]);
done();
});
function fooAction(context, payload, done) {
async.parallel([
function (cb) {
context.executeAction(barAction, {}, cb);
},
function (cb) {
context.executeAction(bazAction, {}, cb);
}
], done);
}
function barAction(context, payload, done) {
context.dispatch('BAR_START');
context.executeAction(someOtherAction, {}, function () {
context.dispatch('BAR_END');
done();
});
}
function bazAction(context, payload, done) {
context.dispatch('BAZ_START');
context.executeAction(someOtherAction, {}, function () {
context.dispatch('BAZ_END');
done();
});
}
function someOtherAction(context, payload, done) {
context.dispatch('SOME_OTHER_ACTION');
done();
}
module.exports = fooAction;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment