KINDLY READ BEFORE MARKING IT AS DUPLICATE
So, I went through all the suggested questions and did almost 2 days research on finding out the reason behind my problem.
Here is what I have -
1. A component named SignIn with some local state connected to redux store.
class SignIn extends React.Component {
constructor(props) {
super(props);
this.state = { isLoading: false };
}
render () {
isLoading
? <SomeLoadingComponent />
: <MainSigninComponent />
}
}
export default ConnectedSignIn = connect(mapStateToProps)(SignIn);
Now as you can see the render output of SignIn changes with the change in local state and I intend to snapshot test both the output.
So I wrote two test cases.
// This one is alright as it test on the default state and renders the actual SigninComponent.
test(`it renders correctly`, () => {
const wrapper = shallow(<ConnectedSignIn {...props} />, { context: { store }});
// .dive() because I intend to snapshot my actual SignIn component and not the connect wrapper.
expect(wrapper.dive()).toMatchSnapshot();
});
Now when I intend to change the state to { isLoading: true } I fire a call to setState like this in second test.
test(`it renders the loading view on setting isLoading state to true`, () => {
const wrapper = shallow(<ConnectedSignIn {...props} />, { context: { store }});
console.log(wrapper.dive().state()) // returns { isLoading: false }
// Again .dive because I want to call setState on my SignIn component
wrapper.dive().setState({ isLoading: true });
console.log(wrapper.dive().state()) // still returns { isLoading: false }
// Tried the callback method to ensure async op completion
wrapper.dive().setState({ isLoading: true }, () => {
console.log(wrapper.dive().state()) // still returns { isLoading: false }
});
});
So going by above code and output, I infer that the shallow wrapper's setState is not working properly, as in not actually updating the state of component.
Just to mention,
1. I also tried using wrapper.dive().instance().setState() as I read it in some question that to update the state of instance, this way would be better. Did not work.
2. I also tried forcing update on shallowed component using wrapper.update() and wrapper.dive().update(). This one also didn't work.
My dependency versions
"react": "16.0.0",
"react-native": "0.50.0",
"react-redux": "^5.0.6",
"enzyme": "^3.3.0",
"enzyme-adapter-react-16": "^1.1.1",
"jest": "21.2.1",
"react-dom": "^16.2.0"
I have already read guides on testing components in isolation by separating them from redux. I did that and my test cases ran fine but I wish to know whether this behaviour is normal or a bug and if someone has been successful in testing state changes as well as render changes of a redux connected component then kindly let me know your approach.
Every time you use dive()
it creates a new wrapper, so all of your calls are referencing a new wrapper rather than the one you updated. Instead, you could try this:
test(`it renders the loading view on setting isLoading state to true, () => {
const wrapper = shallow(<ConnectedSignIn {...props} />, { context: { store }});
const component = wrapper.dive()
component.setState({ isLoading: true });
console.log(component.state()) // should be {isLoading: true}
});
For reference, see Jordan Harband's answer to this Enzyme issue.
This is bad practice. You should test SignIn
and ConnectedSignIn
independently.
1. Create a named export.
// SignIn.js
export class SignIn extends React.Component {
constructor(props) {
super(props);
this.state = {
isLoading: false
};
}
render() {
const { isLoading } = this.state;
return (
(isLoading) ? <SomeLoadingComponent /> : <MainSigninComponent />
);
}
}
export default ConnectedSignIn = connect(mapStateToProps)(SignIn);
2. Import the SignIn
component using the following syntax.
import { SignIn } from 'SignIn.js'
Notice we import SignIn
instead of ConnectedSignIn
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With