Skip to content

Commit

Permalink
Merge pull request #188 from reduxjs/deprecate
Browse files Browse the repository at this point in the history
Add deprecation notice to README and JSDoc
  • Loading branch information
dmitry-zaets authored Oct 18, 2024
2 parents b943c3b + 67cb143 commit 8c1b40a
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 31 deletions.
53 changes: 40 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,26 @@
# redux-mock-store [![Circle CI](https://circleci.com/gh/arnaudbenard/redux-mock-store/tree/master.svg?style=svg)](https://circleci.com/gh/arnaudbenard/redux-mock-store/tree/master)
# Deprecation notice

The Redux team does not recommend testing using this library. Instead, see our [docs](https://redux.js.org/usage/writing-tests) for recommended practices, using a real store.

Testing with a mock store leads to potentially confusing behaviour, such as state not updating when actions are dispatched. Additionally, it's a lot less useful to assert on the actions dispatched rather than the observable state changes.

You can test the entire combination of action creators, reducers, and selectors in a single test, for example:

```js
it('should add a todo', () => {
const store = makeStore() // a user defined reusable store factory

store.dispatch(addTodo('Use Redux'))

expect(selectTodos(store.getState())).toEqual([
{ text: 'Use Redux', completed: false }
])
})
```

This avoids common pitfalls of testing each of these in isolation, such as mocked state shape becoming out of sync with the actual application.

# redux-mock-store [![Circle CI](https://circleci.com/gh/arnaudbenard/redux-mock-store/tree/master.svg?style=svg)](https://circleci.com/gh/arnaudbenard/redux-mock-store/tree/master)

![npm](https://nodei.co/npm/redux-mock-store.png?downloads=true&downloadRank=true&stars=true)

Expand Down Expand Up @@ -36,7 +57,6 @@ const mockStore = configureStore(middlewares)
const addTodo = () => ({ type: 'ADD_TODO' })

it('should dispatch action', () => {

// Initialize mockstore with empty state
const initialState = {}
const store = mockStore(initialState)
Expand Down Expand Up @@ -69,22 +89,21 @@ function success() {
}
}

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

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

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

Expand All @@ -93,41 +112,49 @@ it('should execute fetch data', () => {
```js
configureStore(middlewares?: Array) => mockStore: Function
```

Configure mock store by applying the middlewares.

```js
mockStore(getState?: Object,Function) => store: Function
```

Returns an instance of the configured mock store. If you want to reset your store after every test, you should call this function.

```js
store.dispatch(action) => action
```

Dispatches an action through the mock store. The action will be stored in an array inside the instance and executed.

```js
store.getState() => state: Object
```

Returns the state of the mock store.

```js
store.getActions() => actions: Array
```

Returns the actions of the mock store.

```js
store.clearActions()
```

Clears the stored actions.

```js
store.subscribe(callback: Function) => unsubscribe: Function
```

Subscribe to the store.

```js
store.replaceReducer(nextReducer: Function)
```

Follows the Redux API.

### Old version (`< 1.x.x`)
Expand All @@ -138,9 +165,9 @@ https://github.com/arnaudbenard/redux-mock-store/blob/v0.0.6/README.md

The following versions are exposed by redux-mock-store from the `package.json`:

* `main`: commonJS Version
* `module`/`js:next`: ES Module Version
* `browser` : UMD version
- `main`: commonJS Version
- `module`/`js:next`: ES Module Version
- `browser` : UMD version

## License

Expand Down
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,10 @@
},
"peerDependencies": {
"redux": "*"
},
"prettier": {
"singleQuote": true,
"semi": false,
"trailingComma": "none"
}
}
68 changes: 50 additions & 18 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,37 +1,61 @@
import { applyMiddleware } from 'redux'
import isPlainObject from 'lodash.isplainobject'

const isFunction = arg => typeof arg === 'function'

export function configureStore (middlewares = []) {
return function mockStore (getState = {}) {
function mockStoreWithoutMiddleware () {
const isFunction = (arg) => typeof arg === 'function'

/**
* @deprecated
*
* The Redux team does not recommend using this package for testing. Instead, check out our {@link https://redux.js.org/recipes/writing-tests testing docs} to learn more about testing Redux code.
*
* Testing with a mock store leads to potentially confusing behaviour, such as state not updating when actions are dispatched. Additionally, it's a lot less useful to assert on the actions dispatched rather than the observable state changes.
*
* You can test the entire combination of action creators, reducers, and selectors in a single test, for example:
* ```js
* it("should add a todo", () => {
* const store = makeStore(); // a user defined reusable store factory
*
* store.dispatch(addTodo("Use Redux"));
*
* expect(selectTodos(store.getState())).toEqual([{ text: "Use Redux", completed: false }]);
* });
* ```
*
* This avoids common pitfalls of testing each of these in isolation, such as mocked state shape becoming out of sync with the actual application.
*
* If you want to use `configureStore` without this visual deprecation warning, use the `legacy_configureStore` export instead.
*
* `import { legacy_configureStore as configureStore } from 'redux-mock-store';`
*/
export function configureStore(middlewares = []) {
return function mockStore(getState = {}) {
function mockStoreWithoutMiddleware() {
let actions = []
let listeners = []

const self = {
getState () {
getState() {
return isFunction(getState) ? getState(actions) : getState
},

getActions () {
getActions() {
return actions
},

dispatch (action) {
dispatch(action) {
if (!isPlainObject(action)) {
throw new Error(
'Actions must be plain objects. ' +
'Use custom middleware for async actions.'
'Use custom middleware for async actions.'
)
}

if (typeof action.type === 'undefined') {
throw new Error(
'Actions may not have an undefined "type" property. ' +
'Have you misspelled a constant? ' +
'Action: ' +
JSON.stringify(action)
'Have you misspelled a constant? ' +
'Action: ' +
JSON.stringify(action)
)
}

Expand All @@ -44,11 +68,11 @@ export function configureStore (middlewares = []) {
return action
},

clearActions () {
clearActions() {
actions = []
},

subscribe (cb) {
subscribe(cb) {
if (isFunction(cb)) {
listeners.push(cb)
}
Expand All @@ -63,7 +87,7 @@ export function configureStore (middlewares = []) {
}
},

replaceReducer (nextReducer) {
replaceReducer(nextReducer) {
if (!isFunction(nextReducer)) {
throw new Error('Expected the nextReducer to be a function.')
}
Expand All @@ -73,12 +97,20 @@ export function configureStore (middlewares = []) {
return self
}

const mockStoreWithMiddleware = applyMiddleware(
...middlewares
)(mockStoreWithoutMiddleware)
const mockStoreWithMiddleware = applyMiddleware(...middlewares)(
mockStoreWithoutMiddleware
)

return mockStoreWithMiddleware()
}
}

/**
* Create Mock Store returns a function that will create a mock store from a state
* with the supplied set of middleware applied.
*
* @param middlewares The list of middleware to be applied.
*/
export const legacy_configureStore = configureStore

export default configureStore

0 comments on commit 8c1b40a

Please sign in to comment.