Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

does not work with nested async thunks? #176

Open
bcolloran opened this issue Apr 8, 2020 · 4 comments
Open

does not work with nested async thunks? #176

bcolloran opened this issue Apr 8, 2020 · 4 comments

Comments

@bcolloran
Copy link

bcolloran commented Apr 8, 2020

adapted from the async example in the README:

import configureStore from "redux-mock-store";
import thunk from "redux-thunk";

const middlewares = [thunk]; // add your middlewares like `redux-thunk`
const mockStore = configureStore(middlewares);

// You would import the action from your codebase in a real scenario
function success() {
  return {
    type: "FETCH_DATA_SUCCESS"
  };
}

function success_innerAsync() {
  return {
    type: "FETCH_DATA_SUCCESS--INNER_DISPATCH"
  };
}

function fetchData() {
  return async dispatch => {
    await fetch("/users.json"); // Some async action with promise
    dispatch(success());
    dispatch(fetchMoreData_innerAsync());
  };
}

function fetchMoreData_innerAsync() {
  return async dispatch => {
    await fetch("/more-data.json"); // Some async action with promise
    dispatch(success_innerAsync());
  };
}

it("should execute fetch data", () => {
  const store = mockStore({});

  // Return the promise
  return store.dispatch(fetchData()).then(() => {
    const actions = store.getActions();
    expect(actions).toEqual([success(), success_innerAsync()]);
  });
});

What I'm expecting here is that the thunk fetchData dispatches success() to the store and also dispatches the thunk fetchMoreData_innerAsync; this second thunk should dispatch success_innerAsync() to the store. So i would expect e.g.:

expect(store.getActions()).toEqual([success(), success_innerAsync()]);

But instead, the result is:

Expected value to equal:
  [{"type": "FETCH_DATA_SUCCESS"}, {"type": "FETCH_DATA_SUCCESS--INNER_DISPATCH"}]
Received:
  [{"type": "FETCH_DATA_SUCCESS"}]

Difference:

- Expected
+ Received

Array [
    Object {
      "type": "FETCH_DATA_SUCCESS",
    },
-   Object {
-     "type": "FETCH_DATA_SUCCESS--INNER_DISPATCH",
-   },
  ]

Here's a code sandbox demonstrating:

https://codesandbox.io/s/fragrant-smoke-e0egv?file=/src/index.test.js

This is just a minimal example for to demonstrate, this kind of usage of async thunks in real code seems to work fine in my application. Hence, I don't think I'm abusing Redux/thunks somehow (though please let me know if i am!), but rather that I'm either not understanding how to properly use the mock store, or that there's a bug that only captures the top level of async thunks.

(Or perhaps this will turn out to be a feature request for some kind of new API that expands on .getActions to await all nested async actions/thunks somehow...)

Thanks for any advice!

@bcolloran
Copy link
Author

Update: this definitely seems to be a tricky race condition kind of thing, because inserting a brief delay allows the tests to pass.

Here's an updated sandbox https://codesandbox.io/s/nifty-hofstadter-wnouo

And I'll put the updated passing code here as well for posterity:

import configureStore from "redux-mock-store";
import thunk from "redux-thunk";

const middlewares = [thunk]; // add your middlewares like `redux-thunk`
const mockStore = configureStore(middlewares);

// You would import the action from your codebase in a real scenario
function success() {
  return {
    type: "FETCH_DATA_SUCCESS"
  };
}

function success_innerAsync() {
  return {
    type: "FETCH_DATA_SUCCESS--INNER_DISPATCH"
  };
}

function fetchData() {
  return async dispatch => {
    await fetch("/users.json"); // Some async action with promise
    dispatch(success());
    dispatch(fetchMoreData_innerAsync());
  };
}

function fetchMoreData_innerAsync() {
  return async dispatch => {
    await fetch("/more-data.json"); // Some async action with promise
    dispatch(success_innerAsync());
  };
}

async function wait(ms) {
  return new Promise(resolve => {
    setTimeout(resolve, ms);
  });
}

it("should execute fetch data", async () => {
  const store = mockStore({});

  // Return the promise
  await store.dispatch(fetchData());
  await wait(1000);
  const actions = store.getActions();
  expect(actions).toEqual([success(), success_innerAsync()]);
});

@landonalder
Copy link

landonalder commented May 5, 2020

I'm having this same issue, but the wait makes no difference for me. Redux mock store version is 1.5.4 and react-redux version is 7.1.3. Any additional async actions that are dispatched inside an action creator are never called

Edit: I actually figured out the reason that my async thunk that was dispatched inside the async action I was testing wasn't getting called because there was an exception inside it. Sadly, there's no indication of this except for when you call store.getActions(), I saw an error action that was dispatched with the error message.

@garyee
Copy link

garyee commented Nov 26, 2021

@landonalder thanks!! I had the same error and after reading your comment about the error not showing. I search for and discovered the error. It is working now!

@landonalder
Copy link

@garyee glad to hear this helped :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants