Junit 如何正确地单元测试从FTP服务器获取文件的类

Junit 如何正确地单元测试从FTP服务器获取文件的类,junit,mockito,ftp-client,Junit,Mockito,Ftp Client,事实上,我的问题分为两部分 如何将测试与外部隔离,但仍确保功能正常工作 如何使用Mockito模拟apache commons FtpClient类?当我模拟它时,我会在以下位置得到空: 输入流输入流= ftpClient.retrieveFileStream(ftpParameters.getSourceFileName()) 下面是测试类: 公共类SimpleFtpFileImporterTest{ FtpParameters FtpParameters=新的FtpParameters();

事实上,我的问题分为两部分

  • 如何将测试与外部隔离,但仍确保功能正常工作
  • 如何使用Mockito模拟apache commons FtpClient类?当我模拟它时,我会在以下位置得到空:
  • 输入流输入流= ftpClient.retrieveFileStream(ftpParameters.getSourceFileName())

    下面是测试类:

    公共类SimpleFtpFileImporterTest{
    FtpParameters FtpParameters=新的FtpParameters();
    SimpleFtpFileImporter fileImporter=新的SimpleFtpFileImporter();
    FTPClient ftpMock=mock(FTPClient.class);
    @以前
    公共无效启动(){
    这个.ftpParams.setServer(“10.0.206.126”);
    这个.ftpParams.setPort(21);
    这个.ftpParams.setUserName(“mikola”);
    此.ftpParams.setPassword(“密码”);
    this.ftpParams.setSourceFileName(“readme.txt”);
    }
    @试验
    public void returnNullWhenFileCouldNotBefetchedCompletly()引发IOException{
    当(ftpMock.completePendingCommand())。然后返回(false);
    fileImporter=新的SimpleFtpFileImporter(ftpMock);
    byte[]bytes=fileImporter.downloadFile(ftpParams);
    assertNull(字节);
    }
    }
    
    下面是正在测试的系统:

    公共类SimpleFTFileImporter实现IFileImporter{
    私人FTP客户FTP客户;
    静态记录器Logger=Logger.getLogger(SimpleFtpFileImporter.class);
    静止的{
    PropertyConfigurator.configure(“config/log4j.properties”);
    }
    /**
    *创建传递FtpClient的SimpleFtpFileImporter类实例。
    *此构造函数通过传递任何类型的FTPClient(例如http ftp客户端)来帮助创建单元测试。
    *
    *@param ftpClient一个ftpClient对象
    */
    公共SimpleFtpFileImporter(FTPClient FTPClient){
    this.ftpClient=ftpClient;
    }
    公共SimpleFTFileImporter(){
    }
    /**
    *从指定的FTP服务器获取指定的文件
    *
    *@param ftpParameters承载所需信息的ftpParameters对象
    *@如果成功,返回字节数组中的文件,否则为空
    */
    公共字节[]下载文件(FtpParameters FtpParameters){
    if(this.ftpClient==null)
    this.ftpClient=新的ftpClient();
    如果(!ftpParameters.isPropertyPopulated()){
    warn(“未设置所有FTP参数。执行将停止。”);
    抛出新的FtpParametersNotSetException(“未正确设置Ftp参数”);
    }
    试一试{
    连接(ftpParameters.getServer());
    ftpClient.login(ftpParameters.getUserName(),ftpParameters.getPassword());
    ftpClient.enterLocalPassiveMode();
    ftpClient.setFileType(FTP.BINARY文件类型);
    logger.info(“FTP连接已成功建立。正在准备检索文件:”+ftpParameters.getSourceFileName());
    InputStream InputStream=ftpClient.retrieveFileStream(ftpParameters.getSourceFileName());
    如果(inputStream!=null){
    byte[]bytes=IOUtils.toByteArray(inputStream);
    布尔成功=ftpClient.completePendingCommand();
    logger.info(“收到文件”);
    inputStream.close();
    如果(成功){
    返回字节;
    }否则{
    logger.warn(“无法完成文件获取过程。返回null”);
    返回null;
    }
    }否则{
    logger.warn(“指定了错误的文件名。文件名:”+ftpParameters.getSourceFileName());
    抛出新的RuntimeException(“指定了错误的文件名”);
    }
    }捕获(IOEX异常){
    logger.error(“尝试从远程FTP获取文件时出现问题。消息:“+ex.getMessage()+”\n\r“+ex”);
    }
    返回null;
    }
    
    }


    虽然我提供了所有需要的参数(主机、端口、用户名和密码)

    来回答目前为止可以回答的部分,但模拟的FtpClient对象似乎不适合制作真实的东西-您必须学习如何编写可测试的代码;一个好的起点就是这些

    你们这边已经有一个误解:似乎被嘲笑的FtpClient对象不适合制作真实的东西

    没错。模拟是一个模拟,一个空的测试存根。它没有做任何真实的事情。这就是使用mock的全部意义。它们是空壳,只提供您为它们指定的行为。 我的意思是:Mockito创建的对象不是真正的FtpClient。它只是一个“看起来”像FtpClient的模拟。它没有任何与“真正的”FtpClient“类的连接。换句话说:是的,你可以调用FtpClient拥有的方法,但是它们都是空的。它们什么都不做(好吧,它们做你指定它们做的事情)

    要点是:您可以使用mock来将您与外部实现完全解耦。通过为您的测试代码提供mock,您可以忽略“真实”类正在做的一切

    查看您的代码和“实际”问题:

    为了实现这一点,您必须配置您的模拟,以便在调用
    retrieveFileStream()
    时返回一些有用的

    我的意思是:你已经做到了:

    when(ftpMock.completePendingCommand()).thenReturn(false);
    
    告诉Mockito:当调用
    completePendingCommand()
    时,返回
    false
    。您需要对测试代码调用的每个方法执行此操作

    除此之外,您的代码还存在许多问题,例如:

    public SimpleFtpFileImporter() {
    }
    
    不应为空;相反,您应该执行以下操作:

    public SimpleFtpFileImporter() {
     this(new FtpClient());
    }
    
    答案很简单:默认情况下,字段应为最终字段(除非您有充分理由不将其设为最终字段):

    编译器会告诉你你忘了初始化这个字段!在你的例子中,绝对有
    public SimpleFtpFileImporter() {
     this(new FtpClient());
    }
    
    private final FTPClient ftpClient;