Testing Flux Stores without Jest

3 min read Original article ↗

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