title: Enzyme testing with React/Redux - shallow rendering issues with setState
body: <p>I have a React component that loads another component if it has it's initial local state altered. I can't get a clean test going because I need to set local state AND shallow render so the child component doesn't crash when the new component mounts because the redux store isn't there. It seems those two objectives are incompatible in Enzyme.  </p>
  
  <p>For the child component to display, things need to occur:  </p>
  
  <ol>
  <li>The component needs to receive a "response" props (any string will do)</li>
  <li>The component need to have it's initial "started" local state updated to true. This is done with a button in the actual component. </li>
  </ol>
  
  <p>This is creating some headaches with testing. Here is the actual line that determines what will be rendered: </p>
  
  <pre><code>let correctAnswer = this.props.response ? &lt;div className="global-center"&gt;&lt;h4 &gt;{this.props.response}&lt;/h4&gt;&lt;Score /&gt;&lt;/div&gt; : &lt;p className="quiz-p"&gt;&lt;strong&gt;QUESTION:&lt;/strong&gt; {this.props.currentQuestion}&lt;/p&gt;; 
  </code></pre>
  
  <p>Here is my current Enzyme test: </p>
  
  <pre><code>it('displays score if response and usingQuiz prop give proper input', () =&gt; {
      const wrapper = shallow(&lt;Quiz usingQuiz={true} answers={[]} response={'example'}/&gt;);  
      wrapper.setState({ started: true }) 
      expect(wrapper.contains(&lt;Score /&gt;)).toEqual(true)
  }); 
  </code></pre>
  
  <p>I am using shallow, because any time I use mount, I get this: </p>
  
  <pre><code>Invariant Violation: Could not find "store" in either the context or props of "Connect(Score)". Either wrap the root component in a &lt;Provider&gt;, or explicitly pass "store" as a prop to "Connect(Score)".
  </code></pre>
  
  <p>Because the  component is displayed through the parent, I can not simply select the disconnected version. Using shallow seems to correct this issue, but then I can not update the local state. When I tried this: </p>
  
  <pre><code>it('displays score if response and usingQuiz prop give proper input', () =&gt; {
      const wrapper = shallow(&lt;Quiz usingQuiz={true} answers={[]} response={'example'}/&gt;);  
      wrapper.setState({ started: true })  
      expect(wrapper.contains(&lt;Score /&gt;)).toEqual(true)
  }); 
  </code></pre>
  
  <p>The test fails because shallow doesn't let the DOM get updated. </p>
  
  <p>I need BOTH conditions to be met. I can do each condition individually, but when I need to BOTH 1) render a component inside a component (needs shallow or will freak about the store not being there), and 2) update local state (needs mount, not shallow), I can't get everything to work at once. </p>
  
  <p>I've looked at chats about this topic, and it seems this is a legitimate limitation of Enzyme, at least in 2017. Has this issue been fixed? It's very difficult to test this. </p>
  
  <p>Here is the full component if anyone needs it for reference: </p>
  
  <pre><code>import React from 'react'; 
  import { connect } from 'react-redux'; 
  import { Redirect } from 'react-router-dom';
  import { Transition } from 'react-transition-group';  
  import { answerQuiz, deleteSession, getNewQuestion  } from '../../actions/quiz'; 
  import Score from '../Score/Score'; 
  import './Quiz.css'; 
  
  export class Quiz extends React.Component {
  
      constructor(props) {
          super(props); 
          // local state for local component changes
          this.state = {
              started: false
          }
      }
  
      handleStart() {
          this.setState({started: true})
      }
  
      handleClose() {
          this.props.dispatch(deleteSession(this.props.sessionId))
      }
  
      handleSubmit(event) {  
          event.preventDefault();
          if (this.props.correctAnswer &amp;&amp; this.props.continue) {
              this.props.dispatch(getNewQuestion(this.props.title, this.props.sessionId)); 
          }
          else if (this.props.continue) {
              const { answer } = this.form; 
              this.props.dispatch(answerQuiz(this.props.title, answer.value, this.props.sessionId)); 
          }
          else {
              this.props.dispatch(deleteSession(this.props.sessionId))
          }
      }
  
      render() {  
  
          // Transition styles
          const duration = 300; 
          const defaultStyle = {
              opacity: 0, 
              backgroundColor: 'rgba(0, 0, 0, 0.7)',
              height: '100%', 
              width: '100%', 
              margin: '0px', 
              zIndex: 20, 
              top: '0px', 
              bottom: '0px', 
              left: '0px', 
              right: '0px', 
              position: 'fixed',
              display: 'flex', 
              alignItems: 'center', 
              transition: `opacity ${duration}ms ease-in-out`
          }
  
          const transitionStyles = {
              entering: { opacity: 0 }, 
              entered: { opacity: 1 }
          }
  
          // Response text colors
          const responseClasses = [];
          if (this.props.response) {
              if (this.props.response.includes("You're right!")) {
                  responseClasses.push('quiz-right-response')
              }
              else {
                  responseClasses.push('quiz-wrong-response');
              }
          }
  
          // Answer radio buttons
          let answers = this.props.answers.map((answer, idx) =&gt; (
              &lt;div key={idx} className="quiz-question"&gt;
                  &lt;input type="radio" name="answer" value={answer} /&gt; &lt;span className="quiz-question-label"&gt;{answer}&lt;/span&gt;
              &lt;/div&gt;
          )); 
  
          // Question or answer
          let correctAnswer = this.props.response ? &lt;div className="global-center"&gt;&lt;h4 className={responseClasses.join(' ')}&gt;{this.props.response}&lt;/h4&gt;&lt;Score /&gt;&lt;/div&gt;: &lt;p className="quiz-p"&gt;&lt;strong&gt;QUESTION:&lt;/strong&gt; {this.props.currentQuestion}&lt;/p&gt;; 
  
          // Submit or next 
          let button = this.props.correctAnswer ? &lt;button className="quiz-button-submit"&gt;Next&lt;/button&gt; : &lt;button className="quiz-button-submit"&gt;Submit&lt;/button&gt;; 
  
          if(!this.props.continue) {
              button = &lt;button className="quiz-button-submit"&gt;End&lt;/button&gt;
          }
  
          // content - is quiz started? 
          let content; 
          if(this.state.started) {
              content = &lt;div&gt;
                  &lt;h2 className="quiz-title"&gt;{this.props.title} Quiz&lt;/h2&gt;
                      { correctAnswer }
                      &lt;form className="quiz-form" onSubmit={e =&gt; this.handleSubmit(e)} ref={form =&gt; this.form = form}&gt;
                          { answers }
                          { button }
                      &lt;/form&gt;
                  &lt;/div&gt;
          } else {
              content = &lt;div&gt;
                  &lt;h2 className="quiz-title"&gt;{this.props.title} Quiz&lt;/h2&gt;
                  &lt;p className="quiz-p"&gt;So you think you know about {this.props.title}? This quiz contains {this.props.quizLength} questions that will test your knowledge.&lt;br /&gt;&lt;br /&gt;
                  Good luck!&lt;/p&gt;
                   &lt;button className="quiz-button-start" onClick={() =&gt; this.handleStart()}&gt;Start&lt;/button&gt;
              &lt;/div&gt;
          }
  
          // Is quiz activated? 
          if (this.props.usingQuiz) {
              return ( 
                  &lt;Transition in={true} timeout={duration} appear={true}&gt;
                      {(state) =&gt; (
                              &lt;div style={{ 
                                  ...defaultStyle,
                                  ...transitionStyles[state]
                      }}&gt;
                      {/* &lt;div className="quiz-backdrop"&gt; */}
                          &lt;div className="quiz-main"&gt;
                              &lt;div className="quiz-close" onClick={() =&gt; this.handleClose()}&gt;
                                  &lt;i className="fas fa-times quiz-close-icon"&gt;&lt;/i&gt;
                              &lt;/div&gt;
                              { content } 
                          &lt;/div&gt;
                      &lt;/div&gt;
                  )}
                  &lt;/Transition &gt;
              )  
          } 
          else {
              return &lt;Redirect to="/" /&gt;; 
          }      
      }
  }
  
  const mapStateToProps = state =&gt; ({
      usingQuiz: state.currentQuestion, 
      answers: state.answers, 
      currentQuestion: state.currentQuestion, 
      title: state.currentQuiz,
      sessionId: state.sessionId,  
      correctAnswer: state.correctAnswer, 
      response: state.response,
      continue: state.continue, 
      quizLength: state.quizLength,
      score: state.score, 
      currentIndex: state.currentIndex
  }); 
  
  export default connect(mapStateToProps)(Quiz); 
  </code></pre>
  
  <p>Here is my test using mount (this crashes due to a lack of store): </p>
  
  <pre><code>import React from 'react'; 
  import { Quiz } from '../components/Quiz/Quiz';
  import { Score } from '../components/Score/Score';  
  import { shallow, mount } from 'enzyme'; 
  
      it('displays score if response and usingQuiz prop give proper input', () =&gt; {
          const wrapper = mount(&lt;Quiz usingQuiz={true} answers={[]} response={'example'}/&gt;);  
          wrapper.setState({ started: true })  
          expect(wrapper.contains(&lt;Score /&gt;)).toEqual(true)
      }); 
  });
  </code></pre>