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

Async actions runs partly when testing component #1584

Closed
2 of 10 tasks
gerhardberger opened this issue Mar 17, 2018 · 5 comments
Closed
2 of 10 tasks

Async actions runs partly when testing component #1584

gerhardberger opened this issue Mar 17, 2018 · 5 comments

Comments

@gerhardberger
Copy link

Current behavior

I have an async redux action that does a GET request with the Fetch API, and during the action does two action dispatches (loading and success):

export const fetchServiceConfig = () => async (dispatch) => {
  dispatch(fetchingServiceConfig(true));

  try {
    const response = await fetch(url);

    if (!response.ok) {
      throw new Error(response.statusText);
    }

    const data = await response.json();

    dispatch(fetchedServiceConfig(data));
  } catch (err) {
    dispatch(failedToFetchServiceConfig());
  }
};

I have a container component that passes this action to a presentational component that dispatches it in its componentDidMount:

import { connect } from 'react-redux';

import App from '../../components/App';
import { fetchServiceConfig } from '../../actions/OverviewActions';

const mapStateToProps = (state) => ({
  isLoading: state.overview.isLoading,
  config: state.overview.config
});

const mapDispatchToProps = (dispatch) => ({
  fetchServiceConfig: () => dispatch(fetchServiceConfig())
});

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(App);

I want to test this container component, by observing that it made the appropriate state changes, after it fetched the config:

it('renders the app correctly after fetch', async () => {
  const config = { token: 'dummy token' };
  const store = createStore(rootReducer, applyMiddleware(thunk));
  window.fetch = jest.fn().mockImplementation(() => Promise.resolve({
    ok: true,
    json: () => config
  }));

  const wrapper = shallow(<App store={store} />);

  await wrapper.dive().update();

  expect(wrapper.props().store.getState().overview.config).toEqual(config);
});

The async action starts running after the wrapper.dive().update() call, and returns to the test at the const data = await response.json(), so I cannot make the proper assertions. However the async action completes after the test exited.

Expected behavior

When an async action is run it runs all the way and doesn't return after the second await.

Your environment

API

  • shallow
  • mount
  • render

Version

library version
Enzyme 3.3.0
React 16

Adapter

  • enzyme-adapter-react-16
  • enzyme-adapter-react-15
  • enzyme-adapter-react-15.4
  • enzyme-adapter-react-14
  • enzyme-adapter-react-13
  • enzyme-adapter-react-helper
  • others ( )
@koba04
Copy link
Contributor

koba04 commented Mar 17, 2018

@gerhardberger

await wrapper.dive().update();

update() doesn't return Promise. In this case, you have to wait that const data = await response.json(); is resolved.

#1581 might help how you write a test to wait for async action.

@gerhardberger
Copy link
Author

@koba04 thanks, I looked at #1581 but haven't found solution there.

What I don't understand that if I don't use await before update(), it returns from the async action at the fetch call, which is logical, because that's the first await in the function, but if I use await before update(), it quits at the second await call.

What is an elegant way to wait for such async action?

@koba04
Copy link
Contributor

koba04 commented Mar 17, 2018

@gerhardberger
What about this? It can be used only with your window.fetch implementation though.

it('renders the app correctly after fetch', async () => {
  const config = { token: 'dummy token' };
  const store = createStore(rootReducer, applyMiddleware(thunk));
  window.fetch = jest.fn().mockImplementation(() => Promise.resolve({
    ok: true,
    json: () => config
  }));

  const wrapper = shallow(<App store={store} />);
  // Wait the microtasks(Promises)
  await new Promise(r => setTimeout(r));
  wrapper.dive().update();

  expect(wrapper.props().store.getState().overview.config).toEqual(config);
});

@gerhardberger
Copy link
Author

Thanks, although I think this is not the most elegant solution, maybe we should think of a better way to test for async actions.

The other problem I have, that the config property which can be seen in the state's overview object is passed to the presentational component in the container through mapStateToProps, but at the assertion part of the test, the store is updated, but the property is not? Is this intentional, or how should I wait for the update of the properties?

@koba04
Copy link
Contributor

koba04 commented Mar 19, 2018

@gerhardberger Could you create a repository or example to reproduce it?

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

No branches or pull requests

3 participants