Testing Flux Stores without Jest

Flux stores present an interesting unit testing challenge. From the docs:

By design, stores can't be modified from the outside. They have no setters. The only way new data can enter a store is through the callback it registers with the dispatcher.

To unit test a Flux store without having an entire Flux system in place, you need to be able to call the registered callback directly with a fake action payload. This updates the store's internal state, which you can then make assertions against using the store's public getter methods.

Something like this:

it("adds a user", function () {
registeredCallbackFromStore({
actionType: "ADD_USER",
username: "a_person"
});
const mostRecentUser = UserStore.getMostRecentUser();
expect(mostRecentUser).toBe("a_person");
});

But that registered callback is defined privately inside the store. How do we get a reference to it from our test?

Using Jest

The Flux docs show how to get the registered callback when you write tests with Jest using "this one weird trick":

callback = MyDispatcher.register.mock.calls[0][0];

The docs do a good job of explaining what's happening there, but for the uninitiated:

  • Jest auto-mocks everything unless you tell it not to.
  • MyDispatcher has been thoroughly mocked.
  • When the store calls the mock MyDispatcher.register method, Jest makes information about how it was called (including the arguments it was called with) available on its mock property.
  • The first argument of the first call to MyDispatcher.register is the registered callback.

Using another test framework

If you're using another framework that doesn't do Jest's magical auto-mocking, you can still access the registered callback using the same weird trick. You just need to manually mock things yourself, taking care to mock & require in the correct order.

(This example uses Jasmine, but you could accomplish the same thing with Mocha or your framework of choice.)

beforeEach(function () {
const MyDispatcher = require("my_dispatcher");
spyOn(MyDispatcher, "register");
this.UserStore = require("user_store");
this.registeredCallback = MyDispatcher.register.calls.mostRecent().args[0];
});
describe("UserStore", function () {
it("adds a user", function () {
this.registeredCallback({
actionType: "ADD_USER",
username: "a_person"
});
const mostRecentUser = this.UserStore.getMostRecentUser();
expect(mostRecentUser).toBe("a_person");
});
});

But there's an easier way to get that registered callback...

Modifying modules with rewire

rewire is very cool. It works exactly the same as a plain old CommonJS require, except it...

adds a special setter and getter to modules so you can modify their behaviour for better unit testing.

Those special __set__ and __get__ methods let you go in and set or get anything in the (usually private) top level scope of the rewired module.

So all you need to do to get a reference to the store's registered callback is to go in and __get__ it:

beforeEach(function () {
this.UserStore = rewire("../user_store");
this.registeredCallback = this.UserStore.__get__("registeredCallback");
});
describe("UserStore", function () {
it("adds a user", function () {
this.registeredCallback({
actionType: "ADD_USER",
username: "a_person"
});
const mostRecentUser = this.UserStore.getMostRecentUser();
expect(mostRecentUser).toBe("a_person");
});
});

There's an example repo here showing it in action.

Further reading: Test-driven React: How To Manually Mock Components