title: Mockito Spy SystemCommandTasklet in a Spring Batch Step
body: <p>I am re-factoring a troublesome test I have inherited for a Spring Batch job that calls an external script to perform a task.  The original version of the test substituted the real script with a simple move file script and started the job, then called that script and tested that a file was moved correctly (which served to verify that the script was called with the correct parameters).</p>
  
  <p>My goal for the new version is to eliminate the need to call a real script for this test, instead stubbing and verifying the execution method of the tasklet being used to call the script, relying on Mockito's verify to ensure that the correct parameters are used.</p>
  
  <p>The configuration the job is as follows:</p>
  
  <pre><code>&lt;flow id="job-flow"&gt;
      &lt;step id="preprocess" parent="preprocess-base" next="process"&gt;
          &lt;tasklet&gt;
              &lt;beans:bean class="com.company.project.main.package.PreprocessTasklet"&gt;
                  &lt;beans:property name="doMove" value="false" /&gt;
              &lt;/beans:bean&gt;
          &lt;/tasklet&gt;
      &lt;/step&gt;
      &lt;step id="process" next="postprocess"&gt;
          &lt;tasklet ref="commandTasklet" /&gt;
      &lt;/step&gt;
      &lt;step id="postprocess" parent="postprocess-base" /&gt;
  &lt;/flow&gt;
  
  &lt;bean id="commandTasklet" class="org.springframework.batch.core.step.tasklet.SystemCommandTasklet" scope="step"&gt;
      &lt;property name="command" value="${a.dir}/job_script.sh" /&gt;
      &lt;property name="environmentParams" value="working_dir=#{jobExecutionContext['job.dir']}" /&gt;
      &lt;property name="workingDirectory" value="${b.dir}" /&gt;
      &lt;property name="timeout" value="3600000"/&gt;
  &lt;/bean&gt;
  
  &lt;batch:job id="run-my-script" parent="base-job" incrementer="defaultIncrementer"&gt;
      &lt;batch:flow id="script-job" parent="job-flow" /&gt; 
  &lt;/batch:job&gt;
  </code></pre>
  
  <p>In order to prevent commandTasklet from invoking the actual shell script, I use a BeanPostProcessor to replace it with a spy and stub the execute method.</p>
  
  <pre><code>public class SpiedCommandTaskletPostProcessor implements BeanPostProcessor {
  
  @Override
  public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
      if (SystemCommandTasklet.class.isAssignableFrom(bean.getClass())) {
          SystemCommandTasklet spiedTasklet = Mockito.spy((SystemCommandTasklet) bean);
          try {
              Mockito.doReturn(RepeatStatus.FINISHED).when(spiedTasklet)
                      .execute(Mockito.any(StepContribution.class), Mockito.any(ChunkContext.class));
          } catch (Exception e) {
              e.printStackTrace();
          }
          bean = spiedTasklet;
      }
      return bean;
  }
  
  @Override
  public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
  
      return bean;
  }
  </code></pre>
  
  <p>}</p>
  
  <p>Configuration for the test:</p>
  
  <pre><code>&lt;bean class="com.company.test.package.postprocessor.SpiedCommandTaskletPostProcessor" /&gt;
  </code></pre>
  
  <p>This part works like a charm.  However, this leaves me with two questions: firstly, by the time the PostProcessor runs, the properties (i.e. the parameters for the SystemCommandTasklet I'm trying to verify) have already been presumably set; would I even be able to use Mockito.verify(...) to check for a desired value?
  Secondly, since the SystemCommandTasklet is step-scoped, I can't use autowire to get a hold of it in my test, and trying to look it up in the ApplicationContext just creates a new (and real) instance of the Tasklet outside of the step.  How can I access a step scoped tasklet like this in the context of a JUnit test?</p>
  
  <pre><code>@Test
  public void testLaunch() throws Exception {
      JobExecution jobExecution = getJobLauncher().launchJob(jobParms);
      //Mockito.verify(spiedTasklet).execute(Mockito.any(StepContribution.class), Mockito.any(ChunkContext.class)); spiedTasklet not wired to anything. :(
      assertEquals(ExitStatus.COMPLETED, jobExecution.getExitStatus());
  }
  </code></pre>
  
  <p>Is there a better approach that accomplishes testing that the script is executed with the correct parameters without actually running a script?</p>