When snapshot testing (jest snapshot) a component which is connected to redux store I can export the actual component in addition to the connected component
// User.js
/* ... */
export class User extends React.Component {/* ... */}
/* ... */
export default connect(mapStateToProps)(User);
In the test file I can import the actual component (not the connected version) and do snapshot testing on it.
// User.spec.js
import { User } from './User';
/* ... toMatchSnapshot() testing */
But I run into issues when a connected component is nested inside another connected component. For example, let's say User component is nested inside another connected component -
// Wrapper.js
import User from './User'; // importing the connected version
/* ... */
export class Wrapper extends React.Component {
/* ... */
render() {
return (
<div>
/* ... */
<User />
</div>
);
}
}
export default connect(mapStateToProps)(Wrapper);
When running snapshot test on Wrapper the same way I did on User gives me the following error:
Invariant Violation: Could not find "store" in either the context or props of "Connect(User)". Either wrap the root component in a <Provider>, or explicitly pass "store" as a prop to "Connect(User)".`
Is there any way to shallow render when snapshotting? Or am I doing something wrong?
In this case the best way is to test the Wrapper on its own by just mocking User
import Wrapper from './Wrapper'
jest.mock('./User', () => 'User') // note that the path to user is relative the test file not to the Wrapper.js file.
This will replace User with a simple component with name User.
Just a small tweak to the answer provided by #andreas-köberle as it may produce the error: using incorrect casing. Use PascalCase for React components, or lowercase for HTML elements
To fix that if you want to mock a component it should return a fn. Example reflects multi word naming:
jest.mock('./User', () => () => 'UserThingStuff')
or to return an HTML element:
jest.mock('./User', () => 'user-thing-stuff')
You can add Mock store & then test component withougth connect, but with nested connect Components ↓
Component withought connect,
but include nested connected Elements ->
test with mocking store, which state can configure.↓
before test : npm install redux-mock-store --save-dev
test.js ↓
import configureStore from "redux-mock-store";
import {Provider} from "react-redux";
//create mockStore
let mockStore;
//create Obj for config store
const mockStoreConf = configureStore([]);
//configure store (add states)
mockStore = mockStoreConf({
someStateInMockStore: `SomeValueFromMockKetInMockStore`,
});
const tree = renderer.create(
<Provider store={mockStore}>
<Component />
</Provider>
);
expect(tree).toMatchSnapshot();
Now your Component's nested connected children take state from your mockStore.
Just add needed states on configuration step (//configure store (add states)).
information from https://www.robinwieruch.de/react-connected-component-test
Related
After wrapping a React Component with the appropriate provider, the store is still not found within the jest testing environment. Is there something that I am missing, or another cleaner way to go about this?
This is the practice that is functional for other stores, and I have used with other components, so I don't see a reason why this shouldn't work. The renderer should be creating an object wrapped with the TextContext that it needs to read from in order to populate fields.
Context
import { connect } from 'react-redux';
import React, { createContext } from 'react';
export const TextContext = createContext({});
export function TextProvider({ children, text }) {
return <TextContext.Provider value={text}>{children}</TextContext.Provider>;
}
export const TextConsumer = TextContext.Consumer;
function renderComposition(props) {
const text = {
... // some text objects
};
return (
<TextProvider text={text}>
<Composition {...props} />
</TextProvider>
);
}
target failing line
beforeEach(() => {
...
subject = mount(renderer.create(renderComposition(props)));
...
)};
with error of
Invariant Violation: Could not find "store" in either the context or props of "Connect(Composition)". Either wrap the root component in a <Provider>, or explicitly pass "store" as a prop to "Connect(Composition)".
I guess your component requires mocked store, you can provide it by creating mockReduxState.js
import configureMockStore from "redux-mock-store";
export const createMockStore = state => configureMockStore()(state);
Updating the failing test by passing mockedStore.
beforeEach(() => {
...
let updatedProp = {...props, store:createMockStore};
subject = mount(renderer.create(renderComposition(updatedProp)));
...
)};
Turns out the issue was unrelated, I was importing the component rather than the connected container, so the store was never getting set. Names are half of the battle turns out. The mocking the store option is also a great way to handle this 👍 thanks paragxvii
I am trying to test the connected component(react-redux) with jest-enzyme. I am using react-redux-mock store. When I run my test to find one div in the component it gives me this error.
Invariant Violation: Passing redux store in props has been removed and does not do anything. To use a custom Redux store for specific components, create a custom React context with React.createContext(), and pass the context object to React-Redux's Provider and specific components like: <Provider context={MyContext}><ConnectedComponent context={MyContext} /></Provider>. You may also pass a {context : MyContext} option to connect
I did mount and tested just component without redux it works but I want to do a > shallow test.
describe("Input Component", () => {
let wrapper;
let store;
beforeEach(() => {
store = mockStore(initialState);
wrapper = shallow(<Input store={store} />);
});
it("should rendder without error", () => {
expect(wrapper.find("div")).toHaveLength(1);
});
});
How do you import your component?
if your are importing it with import App from './yourpath/App' for example, ou're actually holding the wrapper component returned by connect(), and not the App component itself.
In order to be able to test the App component itself without having to deal with the decorator, you must to also export the undecorated component:
import { connect } from 'react-redux'
// Use named export for unconnected component (for tests)
export class App extends Component {
/* ... */
}
// Use default export for the connected component (for app)
export default connect(mapStateToProps)(App)
And import it in your test file like that:
import { App } from './yourpath/App'
I'm using React and Redux. I have a component which loads ChildComponent and depending on Redux's state will also load MainComponent
const ChooseIndex = ({ appInitMount }) => {
return (
<>
<ChildComponent />
{!appInitMount && <MainComponent />}
</>
);
};
const mapStateToProps = ({ main }) => {
return {
appInitMount: main.appInitMount
};
};
export default connect(
mapStateToProps,
mapDispatchToProps
)(ChooseIndex);
I'm trying to write a test to check that ChildComponent is loaded:
import React from "react";
import { render } from "react-testing-library";
import ChooseIndex from "../choose-index";
test("ChooseIndex should call ChildComponent", () => {
const wrapper = render(
<ChooseIndex />
);
});
I get this error:
Error: Uncaught [Invariant Violation: Could not find "store" in either
the context or props of "Connect(ChooseIndex)". Either wrap the root
component in a , or explicitly pass "store" as a prop to
"Connect(ChooseIndex)".]
Should I mock Redux by passing an object literal to ChooseIndex? Or should I create a Redux store (as my real application does) for every test?
Try to render your component like this:
render(
<Provider store={store}>
<ChooseIndex />
</Provider>
)
And pass the actual store you use in your app. In this way, you're testing the real logic that you'll use in production. You also don't care what actions get dispatched and what's in the state. You look at what gets rendered and interact with the UI—which is what matters in the end.
Separating the component from Redux and testing the two in isolation is against the whole point of react-testing-library. You want to test your app as a real user would.
If you check out the writing tests section of the redux docs, there is an example of testing a connected component.
when you import it [A redux connected component], you're actually holding the wrapper component returned by connect(), and not the App component itself. If you want to test its interaction with Redux, this is good news: you can wrap it in a with a store created specifically for this unit test. But sometimes you want to test just the rendering of the component, without a Redux store.
In order to be able to test the App component itself without having to deal with the decorator, we recommend you to also export the undecorated component
As with most unit tests, you really want to be testing your components, and not that redux is working correctly. So the solution for you is to export both the component and the connected component, while only testing the component itself, and providing whatever props redux is passing to your component.
import { connect } from 'react-redux'
// Use named export for unconnected component (for tests)
export class App extends Component {
/* ... */
}
// Use default export for the connected component (for app)
export default connect(mapStateToProps)(App)
Issue: I am getting an error when attempting to inject a MST store into react class component:
Error: Uncaught [Error: MobX injector: Store 'auth' is not available! Make sure it is provided by some Provider
the error stems from the LoginForm component posted below, and is nested under another component (not sure if this is the issue, as to say each component needs a <Provider>?)
component hierarchy:
Application (contains no Provider)
|__ Authentication (contains no Provider)
|__LoginForm
a little code:
import { AuthenticationStore } from './stores/AuthenticationStore'
const auth = AuthenticationStore.create()
ReactDOM.render(
<Provider auth={auth}>
<Application />
</Provider>,
document.getElementById('root')
);
//Application
const Application = inject('auth')(observer(class Application extends
Component {
render() {
console.log(this.props.auth) // logs the store as expected
return (
<div className={styles.applicationContainer}>
<Authentication/>
</div>
)
}
}))
export default Application
// LoginForm
const LoginForm = inject('auth')(observer(class LoginForm extends Component {
render() {
return (
<div></div>
)
}
}))
export default LoginForm
and to the question:
If not this, what is the preferred protocol for passing the store to child components (without passing as props)?
As always, thanks in advance for any and all input. If this is a duplicate (I couldn't find this particular error), please mark as such and I will update accordingly...
I've been trying to figure out how to do simple snapshot testing for a few components that have mobx stores injected into them. Here's an example:
At the root i have a <Provider> wrapping the whole shebang at my final ReactDOM.render() in the entry point. (not shown here)
// component.js
...{imports}...
#inject('mystore')
#observer
export default class extends React.Component {
render() {
return (
<div>Stuff</div>
)
}
}
// component.test.js
import React from 'react';
import ReactDOM from 'react-dom';
import renderer from 'react-test-renderer';
import Component from './'
it('renders a snapshot', () => {
const tree = renderer.create(<Component/>).toJSON();
expect(tree).toMatchSnapshot();
});
I get a failed test because it's missing a store provided up the tree. I've tried exporting an "undecorated" component like so:
// in component.js
...{same as above}...
export const undecorated = Component
and then importing the undecorated component in my snapshot test, however this doesn't work.
Ideas?
You should be able to pass the store explicitly like so:
const tree = renderer.create(
<Component.wrappedComponent myStore={store}/>).toJSON();
)