Cannot read property '.then' of undefined when testing async action creators with redux and react
Asked Answered
I

4

27

I'm trying to write some test using react, redux-mock-store and redux, but I keep getting and error. Maybe because my Promise has not yet been resolved?

The fetchListing() action creator actually works when I try it on dev and production, but I'm having problems making the test pass.

error message

(node:19143) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 3): SyntaxError
(node:19143) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
 FAIL  src/actions/__tests__/action.test.js
  ● async actions › creates "FETCH_LISTINGS" when fetching listing has been done

    TypeError: Cannot read property 'then' of undefined

      at Object.<anonymous> (src/actions/__tests__/action.test.js:44:51)
          at Promise (<anonymous>)
      at Promise.resolve.then.el (node_modules/p-map/index.js:42:16)
          at <anonymous>
      at process._tickCallback (internal/process/next_tick.js:169:7)

  async actions
    ✕ creates "FETCH_LISTINGS" when fetching listing has been done (10ms)

action/index.js

// actions/index.js
import axios from 'axios';

import { FETCH_LISTINGS } from './types';

export function fetchListings() {

  const request = axios.get('/5/index.cfm?event=stream:listings');

  return (dispatch) => {
    request.then(( { data } ) => {
      dispatch({ type: FETCH_LISTINGS, payload: data });
    });
  }
};

action.test.js

// actions/__test__/action.test.js

import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import { applyMiddleware } from 'redux';
import nock from 'nock';
import expect from 'expect';

import * as actions from '../index';
import * as types from '../types';


const middlewares = [ thunk ];
const mockStore = configureMockStore(middlewares);

describe('async actions', () => {
  afterEach(() => {
    nock.cleanAll()
})


it('creates "FETCH_LISTINGS" when fetching listing has been done', () => {
  nock('http://example.com/')
    .get('/listings')
    .reply(200, { body: { listings: [{ 'corpo_id': 5629, id: 1382796, name: 'masm' }] } })

    const expectedActions = [
      { type: types.FETCH_LISTINGS }, { body: { listings: [{ 'corpo_id': 5629, id: 1382796, name: 'masm' }] }}
    ]

    const store = mockStore({ listings: [] })

    return store.dispatch(actions.fetchListings()).then((data) => {
      expect(store.getActions()).toEqual(expectedActions)
    })
  })
})
Invoice answered 5/7, 2017 at 13:24 Comment(0)
P
17

store.dispatch(actions.fetchListings()) returns undefined. You can't call .then on that.

See redux-thunk code. It executes the function you return and returns that. The function you return in fetchListings returns nothing, i.e. undefined.

Try

return (dispatch) => {
    return request.then( (data) => {
      dispatch({ type: FETCH_LISTINGS, payload: data });
    });
  }

After that you will still have another problem. You don't return anything inside your then, you only dispatch. That means the next then gets undefined argument

Pricecutting answered 5/7, 2017 at 13:28 Comment(5)
fetchListings() actually works when I use it - I can see the data on my app - but is there a better way to write that function? What about test itself how can I write it?Invoice
Writing tests for async code can be challenging. One of the main selling points of redux-saga over redux-thunk is the ease of testing, so you might want to check that library out.Bin
Sure redux-saga is easier to test because it just uses plain objects, but redux-thunk should be easy enough to test as well. The problem here is how he handles promises (and a few other things). See my updated answerPricecutting
Have you found a solution for the tests? I have the same test setup and getting the same error.Merriott
@SzilardMagyar I honestly can't remember. Tis was an old project I was working on. I will check again in the following days and if I find something I'll post it here :)Invoice
T
9

I also know this is an old thread but you need to make sure you return the async action inside of your thunk.

In my thunk I needed to:

return fetch()

the async action and it worked

Thurifer answered 24/12, 2017 at 3:53 Comment(0)
S
4

Your action creator should return a promise as shown below:

// actions/index.js
import axios from 'axios';

import { FETCH_LISTINGS } from './types';

export function fetchListings() {
  return (dispatch) => {
    return axios.get('/5/index.cfm?event=stream:listings')
    .then(( { data } ) => {
      dispatch({ type: FETCH_LISTINGS, payload: data });
    });
  }
};
Sporogenesis answered 9/6, 2019 at 15:3 Comment(0)
B
3

I know this is an old thread. But I think the issue is that your action creator is not asynchronous.

Try:

export async function fetchListings() {
  const request = axios.get('/5/index.cfm?event=stream:listings');
  return (dispatch) => {
    request.then(( { data } ) => {
      dispatch({ type: FETCH_LISTINGS, payload: data });
    });
  }
}
Bushelman answered 17/12, 2017 at 0:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.