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

Mock Store State is not being updated #71

Closed
adamyonk opened this issue Sep 12, 2016 · 18 comments
Closed

Mock Store State is not being updated #71

adamyonk opened this issue Sep 12, 2016 · 18 comments

Comments

@adamyonk
Copy link

adamyonk commented Sep 12, 2016

I'm having the same issue as #45. Maybe I'm not understanding this correctly, but does mock-store actually run the actions through the reducers anywhere? I'm not seeing how they're ever connected with a setup like this:

import configureStore from 'redux-mock-store'
import thunk from 'redux-thunk'
const mockStore = configureStore([thunk])
const store = mockStore({ ...state })
console.log(store.getState()) // => { ...state }
store.dispatch(someAction())
console.log(store.getActions()) // => [ someAction() ] The action does show up here!
console.log(store.getState()) // => { ...state } But this is the same unchanged state as above
@dmitry-zaets
Copy link
Collaborator

dmitry-zaets commented Sep 12, 2016

As you can see from codebase - it doesn't execute any reducers.
it even doesn't accept any of them during setup.
It only notifies the list of subscribers.
So the state will be always the same.

@adamyonk
Copy link
Author

OK, that's what I was thinking. Is there a reason for getState in the API then?

@dmitry-zaets
Copy link
Collaborator

dmitry-zaets commented Sep 12, 2016

I'm not sure, but I think the main reason is backward compatibility with redux API.
As you can see here - the value of getStore is just bypassed.

@adamyonk
Copy link
Author

Ah, OK. So I was thinking that I'd be able to test the state along with testing what actions were triggered, but redux-mock-store is really just for the actions part. Seeing getState it in the docs threw me, thanks for clarifying!

@adamyonk
Copy link
Author

The one place this kind of falls down is when you're testing something that uses an async action creator (thunks), that change the state before triggering other actions.

@dmitry-zaets
Copy link
Collaborator

dmitry-zaets commented Sep 12, 2016

Yep, redux-mock-store is just for the testing actions part. To test reducer and it states you don't need any kind of mock. You just need to test reducer as a pure function (Input -> Output), that's all.

@dmitry-zaets
Copy link
Collaborator

dmitry-zaets commented Sep 12, 2016

For testing, complex flows with async actions you can check https://github.com/redux-things/redux-actions-assertions, it was designed for that kind of testing.

@adamyonk
Copy link
Author

That is exactly what I needed. Thanks, @dmitry-zaets!

@dmitry-zaets
Copy link
Collaborator

Always welcome!

@jhalborg
Copy link

jhalborg commented Oct 3, 2017

God damnit, I just found this after pulling my hair for two hours. I think it should be added to the docs very explicitly that the mock stores' state is never changed, as one would assume it does when it's in the API. Reading the discussion here, it does make sense, but it's potentially a timewaster

@ohtangza
Copy link
Contributor

This is definitely something that should have documented in README.md.

@dmitry-zaets Can I document it and make a pull request not to confuse the newbie here?

@dmitry-zaets
Copy link
Collaborator

@ohtangza Sure, would be happy to merge the PR

arnaudbenard pushed a commit that referenced this issue Feb 1, 2018
* Improve documentation

* Elaborate wordings

* Remove sponsors
@pzhine
Copy link

pzhine commented Mar 1, 2018

Testing your reducer alongside your actions with redux-mock-store is as simple as running your reducer function on the state that you pass to the mock store and the action you expect:

import actions from './actions'
import reducer from './reducer'
import initialState from './initialState'

const state = { ...initialState, numbers: '23' }
const expectedAction = { type: 'DELETE_LAST_NUMBER', payload: '*' }
return store.dispatch(actions.pressKey('*')).then(() => {
  expect(store.getActions()[0]).toEqual(expectedAction)
  expect(reducer(state, expectedAction)).toEqual({ ...state, numbers: '2' })
})

@tutts
Copy link

tutts commented Mar 27, 2019

I had the same issue, and found I kept writing the same lines over and over to test store, so wrote a tiny lib on top of redux-mock-store to capture snapshots of reducer states here

@mbellman
Copy link

mbellman commented Jul 25, 2019

Incidentally, this can be achieved via the following:

import configureMockStore from 'redux-mock-store';
import appReducer from 'path/to/your/appReducer';

const createMockStore = configureMockStore([ ... ]);

const createState = initialState => actions => actions.reduce(appReducer, initialState);

...

it('...', () => {
  const initialState = createState({ ... } );
  const store = createMockStore(initialState);

  store.dispatch(...);

  // Assert as you prefer
  expect(store.getState()).toEqual(...);
  expect(store.getState()).toMatchObject(...);
  expect(store.getActions()).toEqual([ ... ]);
});

This is particularly useful if you're dispatching thunks which rely on getState() to control their behavior. It's also suspiciously close to just using your real store and testing dispatches against that, although you can still customize the initial state and check dispatched actions more easily.

@JohnDDuncanIII
Copy link

JohnDDuncanIII commented Aug 9, 2019

Modified version of pzhine's answer that supports Immutable and calling the same action creator multiple times in a different action creator (we have an async action creator that we pass a synchronous action creator to).

import { fromJS } from "immutable"
import mockStore from "__tests__helpers/mockStore"

import { setTranscriptsValues } from "shared/profiles/event/event-profile-action-creators"
import { eventReducer } from "shared/profiles/event/event-profile-reducer"

let store = mockStore({})

const setTranscriptsValuesHelper = ({
    values,
    profileId,
}) => {
    store = mockStore(
        eventReducer(
            fromJS(store.getState()),
            setTranscriptsValues({
                values,
                profileId,
            })
        )
    )
}

beforeEach(() => {
    store = mockStore({})
})

it("loadInlines", () => {
    loadInlines({
 		...stuff
        setTranscriptsValues: setTranscriptsValuesHelper,
    }).then(() => {
        console.log(store.getState())
    })
})

@agolendukhin
Copy link

@mbellman thanks man! It just works fine.

@AlejandroSilva
Copy link

I finally get to test the final store state like this:

const actions = [ {type: 'action1, payload: 1}, {type: 'action2, payload: 2}]
const finalState = actions.reduce(myCoolReducer, INITIAL_STATE)
const expectedState = {...}
expect(expectedState).toEqual(finalState)

The reducer is a pure function, so in order to get the final state we just has to call every action on top of an initial state. This way every action receive the previous state and return a new one until the end of the array.

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

10 participants