15

Testing Flux Stores without Jest

 3 years ago
source link: https://www.bensmithett.com/testing-flux-stores-without-jest/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Testing Flux Stores without Jest

Thursday, February 19th 2015

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK