JUnit/Mockito:如何模拟或创建私有成员变量

问题描述

我有一个私有字符串变量 filePath,它将在 SpringBoot 的 execute(..) 方法中设置,然后该值将在另一个方法中使用,该方法将从该 execute(..) 内部调用

@Component("filebatchjobtask")
public class FileBatchJobTask extends BaseFileBatchJobTask implements tasklet {


    private String filePath;   // PRIVATE VARIABLE THAT WILL BE USED IN A CALL 

    private static final CalLogger LOGGER = CalLoggerFactory.getLogger(FileBatchJobTask.class);

    @Override
    public RepeatStatus execute(final StepContribution stepContribution,final ChunkContext chunkContext) throws Exception {

        // INITIALIZE PRIVATE VARIABLE HERE
        filePath  = chunkContext.getStepContext().getJobParameters().get(Constants.FILEPATH).toString();
        
        processeFile();  // METHOD CALL WHERE FILEPATH INITIALIZED ABOVE WILL BE USED

        return RepeatStatus.FINISHED;
    }

    @Override
    protected void processeFile() throws IOException {
        LOGGER.warn("FileBatchJobTask:processeFile():: Directory to process files: " + filePath);
        File[] filelist = geteFiles(filePath);  // THIS IS THE CALL I WANT TO MOCK
        if (filelist == null || filelist.length < 1) {
            LOGGER.warn("FileBatchJobTask: No eFiles available to process");
            return;
        }

        LOGGER.warn("Total number of files to process: " + filelist.length);
}

对应的测试如下:

//@RunWith(powermockrunner.class)
@RunWith(MockitoJUnitRunner.class)
public class FileBatchJobTaskTest extends BaseFileBatchJobTaskTest {

    @InjectMocks
    FileBatchJobTask fileBatchJobTask;

    @Override
    BaseFileBatchJobTask createFileBatchJobTask() {
        return fileBatchJobTask;
    }

    @Test
    public void processeFile() {
        BaseFileBatchJobTask batchJobTask = Mockito.spy(createFileBatchJobTask());
        
        // THIS resourceDir is the I want to use instead of filePath variable in tests here and pick file from this test resource path
        Path resourceDir = Paths.get("src","test","resources","data","validation");
        resourcePath = resourceDir.toFile().getAbsolutePath();

        File fileDir = new File(resourcePath);
        File[] files = fileDir.listFiles(new FileFilter() {
            @Override
            public boolean accept(final File pathname) {
                String name = pathname.getName().toLowerCase();
                return name.endsWith(".xml") && pathname.isFile();
            }
        });
        doReturn(files).when(batchJobTask).geteFiles(anyString());  // THIS IS THE CALL I AM TRYING TO MOCK
        

        try {
            
            fileBatchJobTask.processeFile();
            Assert.assertTrue(true);
        } catch (...) {

        }

}

这是基类

class BaseFileBatchJobTask {

    protected File[] geteFiles(final String eFileDirPath) {
        File fileDir = new File(eFileDirPath);   // NPE as eFileDirPath is null
        File[] files = fileDir.listFiles(new FileFilter() {
            @Override
            public boolean accept(final File pathname) {
                String name = pathname.getName().toLowerCase();
                return name.endsWith(".xml") && pathname.isFile();
            }
        });

        return files;
    }

}

ERROR:当测试运行时,我得到 NPE,执行 getEFiles() 并且 filePath 为空。由于我是在嘲笑,它不应该进入该方法的实际实现。但是,它似乎没有像预期的那样被嘲笑,因此需要帮助来解决问题。

还查找了很多 SO 帖子,但无法弄清楚问题所在,因此如果您不知道答案,请不要将其标记为重复 :)

解决方法

您需要在 jobTask 的间谍版本上调用 processeFile(),而不是在原始版本上调用。想想间谍是被监视对象的包装器,它拦截模拟调用。

简而言之,只需在 batchJobTask 块中使用 try-catch,如下所示:

    try {    
    
        batchJobTask.processeFile();
        Assert.assertTrue(true);
    } catch (...) {

    }