Kevin Whitaker
Kevin Whitaker

Reputation: 13415

Jasmine: How to test a function which returns another function?

Solution below

Question says it all. Given this function:

export function fetchBuilds() {
  return dispatch => {
    touchCookie();
    dispatch(requestBuilds());
    return request.get('/builds')
      .withCredentials()
      .use(AuthIntercept)
      .then((response) => {
        dispatch(receiveBuilds(response));
      }, (error) => {
        dispatch(receiveBuildsError(error.message));
      });
  };
}

what's the most effective testing strategy? I plan on mocking the AJAX request, but how do I assert that the return is what I expect?

Following on the heels of that, I'm also at a loss as to the best way to test the control flow for the function; or is that even something I should worry about?

Solution based on @bkonkle's suggestion

The setTimeout calls are ugly, but they seem to get the job done.

describe('Build actions', () => {
  const testBuilds = [
    {id: 1, name: 'some name'}
  ];

  beforeEach(() => {
    dispatchSpy = jasmine.createSpy('dispatch').and.callFake((cb) => {
      switch (cb.type) {
      case types.REQUEST_BUILDS:
        return {
          type: types.REQUEST_BUILDS
        };

      case types.RECEIVE_BUILDS:
        return {
          type: types.RECEIVE_BUILDS,
          builds: testBuilds
        };

      default:
        return null;
      }
    });

    cookieSpy = spyOn(userActions, 'touchCookie');
    requestSpy = spyOn(actions, 'requestBuilds').and.callThrough();
    receiveSpy = spyOn(actions, 'receiveBuilds').and.callThrough();
  });

  it('should be able fetch builds', () => {
    jasmine.Ajax.install();
    const result = actions.fetchBuilds();
    result(dispatchSpy);

    expect(cookieSpy).toHaveBeenCalled();
    expect(dispatchSpy).toHaveBeenCalledWith(actions.requestBuilds());
    expect(requestSpy).toHaveBeenCalled();

    let request = jasmine.Ajax.requests.mostRecent();
    expect(request.url).toBe(`/builds`);
    request.respondWith({
      status: 200,
      contentType: 'application/json',
      responseText: JSON.stringify(testBuilds)
    });

    setTimeout(() => {
      expect(dispatchSpy).toHaveBeenCalledWith(actions.receiveBuilds(testBuilds));
      setTimeout(() => {
        expect(receiveSpy).toHaveBeenCalledWith(testBuilds);
      }, 10);
    }, 1);
  });
});

Upvotes: 0

Views: 2055

Answers (1)

Brandon Konkle
Brandon Konkle

Reputation: 747

This is definitely not an easy thing to test. I'd use Sinon for spies and stubs, and make it an async test. First, I would call the returned function in my test body. I would spy on touchCookie, dispatch, and requestBuilds, and make sure that they were called. I'd make sure that the return value of the returned function is a promise, and attach a .then in the test body that checks to see that dispatch was called with the results of receiveBuilds and receiveBuildsError, both of which I would stub. I would also spy on the Ajax call and make sure the correct url was requested.

Your test code will definitely be more lengthy than your actual function, but that's not really a bad thing. :-)

Upvotes: 1

Related Questions