Testing Your Subscriptions

Run it like a Function

Since subscriptions are just functions, you should easily be able to execute them. The signature of every subscription is (...params) => (dispatch) => cleanUpFn, so we know how to get the code to execute, and how to stop it, so what's preventing us from doing that? Let's take this subscription as an example:

mySubscription.js
export const mySubscription = (dispatch, delay, onIntervalAction) => dispatch => {
  const handle = setInterval(dispatch, delay, onIntervalAction);
  return () => {
    clearInterval(handle);
  };
};

Maybe a test could look something like this:

mySubscription.spec.js
import { mySubscription } from './mySubscription.js';
import * as sinon from 'sinon';

describe('mySubscription', () => {
  it('creates an interval', () => {
    expect.assertions(1);
    sinon.spyOn(global, 'setInterval');
    const dispatch = sinon.fake();
    const myAction = () => {};
    
    const cleanup = mySubscription(dispatch, 1000, myAction);
    expect(global.setInterval.mock.calls).toHaveLength(1);
    cleanup();
    expect(dispatch.calledWith, [myAction]);
    
    setInterval.restore();
  });
});

End to End Testing

Calling your subscription in a test can get you fairly far, but the real test is when your application expects some sort of payload from your subscription.

myApplication.spec.js
import { app, effects } from 'ferp';

import { intervalSub } from './intervalSub.js';

const testify = (expectedActions, done) => (_, actionAnnotation) => {
  const actionName = expectedActions.unshift();
  expect(actionAnnotation).toBe(actionName);
  
  if (expectedActions.length === 0) {
    return done();
  }
};

describe('myApplication', () => {
  it('runs an effect from mySubscription', (done) => {
    const myAction = state => [false, effects.none()];
    
    app({
      init: [true, effects.none()],
      observe: testify([
        'ferpAppInitialization', // this is just the action/annotation of `init`
        'myAction',
      ], done),
      subscribe: state => [
        state && [intervalSub, 1000, myAction],
      ],
    });
  });
});

Last updated