2017-02-03 4 views
1

実際、私の質問は2つの部分に分かれています。FTPサーバーからファイルを取得するクラスを適切に単体テストする方法

  1. 私のテストを外部から隔離しても、その機能が動作することを確認するにはどうすればよいですか?
  2. Mockitoを使用してFtpClientクラスを共有するにはどうすればよいですか?私はそれを嘲笑するとき、私は、nullを取得:

InputStream InputStreamは= ftpClient.retrieveFileStream(ftpParameters.getSourceFileName());

はここでのテストクラスです:

public class SimpleFtpFileImporterTest { 
    FtpParameters ftpParams =new FtpParameters(); 
    SimpleFtpFileImporter fileImporter=new SimpleFtpFileImporter(); 
    FTPClient ftpMock= mock(FTPClient.class); 



@Before 
public void startup(){ 
    this.ftpParams.setServer("10.0.206.126"); 
    this.ftpParams.setPort(21); 
    this.ftpParams.setUserName("mikola"); 
    this.ftpParams.setPassword("password"); 
    this.ftpParams.setSourceFileName("readme.txt"); 
} 


@Test 
public void returnNullWhenFileCouldNotBeFetchedCompletely() throws IOException{ 
    when(ftpMock.completePendingCommand()).thenReturn(false); 
    fileImporter=new SimpleFtpFileImporter(ftpMock); 
    byte[] bytes= fileImporter.downloadFile(ftpParams); 
    assertNull(bytes); 
} 

} 

そしてここでは、テスト対象のシステムです:

public class SimpleFtpFileImporter implements IFileImporter { 

private FTPClient ftpClient; 

static Logger logger = Logger.getLogger(SimpleFtpFileImporter.class); 

static { 
    PropertyConfigurator.configure("config/log4j.properties"); 
} 

/** 
* Creates a SimpleFtpFileImporter class instance passing an FtpClient. 
* This constructor helps create unit tests by passing any kind of FTPClient, eg. an http ftp client. 
* 
* @param ftpClient An FTPClient object 
*/ 
public SimpleFtpFileImporter(FTPClient ftpClient) { 
    this.ftpClient = ftpClient; 
} 

public SimpleFtpFileImporter() { 

} 
/** 
* Gets the file specified from the specified FTP server 
* 
* @param ftpParameters An FtpParametrs object that bears the needed information 
* @return File in byte array if successful, otherwise null 
*/ 
public byte[] downloadFile(FtpParameters ftpParameters) { 
    if (this.ftpClient == null) 
     this.ftpClient = new FTPClient(); 
    if (!ftpParameters.isProperlyPopulated()) { 
     logger.warn("Not all FTP parameters have been set. Execution will halt."); 
     throw new FtpParametersNotSetException("Ftp parameters not properly set."); 
    } 
    try { 
     ftpClient.connect(ftpParameters.getServer()); 
     ftpClient.login(ftpParameters.getUserName(), ftpParameters.getPassword()); 
     ftpClient.enterLocalPassiveMode(); 
     ftpClient.setFileType(FTP.BINARY_FILE_TYPE); 
     logger.info("FTP connection succesfully established. Preparing to retrieve file:"+ftpParameters.getSourceFileName()); 

     InputStream inputStream = ftpClient.retrieveFileStream(ftpParameters.getSourceFileName()); 
     if (inputStream != null) { 
      byte[] bytes = IOUtils.toByteArray(inputStream); 
      boolean success = ftpClient.completePendingCommand(); 
      logger.info("File received"); 
      inputStream.close(); 
      if (success) { 
       return bytes; 
      } else{ 
       logger.warn("File fetching process could not be through. Returning null."); 
       return null; 
      } 
     }else{ 
      logger.warn("Wrong file name specified. File name:"+ftpParameters.getSourceFileName()); 
      throw new RuntimeException("Wrong file name specified"); 
     } 


    } catch (IOException ex) { 
     logger.error("Problem while trying to get file from remote FTP. Message: " + ex.getMessage() + " \n\r" + ex); 
    } 

    return null; 
} 

}

嘲笑FtpClientオブジェクトは本物を作るのに適していないようで、必要なすべてのパラメータ(ホスト、ポート、ユーザー名とパスワード)を提供しますが、

+1

私はちょっと驚いています。この質問は初心者から「なぜ私のコードがうまくいかないのか」と尋ねたようなものです。真剣に:[mcve]を提供してください。あなたが求めているのは、土地を嘲笑いながら "日常のビジネス"です。だから確かにあなたのコードにいくつかの単純なバグがあります;-) – GhostCat

+0

Nnnnnn [OK]を、私はいくつかのコードを追加します –

+0

私の答えは私の更新をしてください。 – GhostCat

答えて

3

これまでに回答できる部分に答えるには、書く方法を学ぶ必要があります。テスト可能コードです。良い出発点はvideosです。

あなたの側にはすでに誤解があります:FtpClientオブジェクトが実際に作成するのに適していないようです。

正確に。モックはモック、空のテストスタブです。本当のことは何もしません。それがモックを使用する全体のポイントです。それらは空のシェルで、何もしませんが、指定した動作を提供します。 意味:Mockitoによって作成されたオブジェクトはではなく、真のFtpClientです。 模擬は、FtpClientのように「見える」ものです。つまり、実際のFtpClientクラスへの接続はで、はありません。つまり、FtpClientが持つメソッドを呼び出すことができますが、すべて空のです。ポイントは)あなたが彼らの操作を行うためにを指定何を

に:。。あなたは、外部実装から完全にデカップルあなたにモックを使用してテスト対象のコードにモックを与えることによって、あなたは単にすることができます

を無視してください。

あなたのコードと「実際の」問題を見てみる:その仕事をするためには

InputStream inputStream = ftpClient.retrieveFileStream(

、あなたが設定する必要がモックretrieveFileStream()が呼び出されたときに、何か役に立つを戻すために!私は何を意味

です:あなたはすでに実行します。

when(ftpMock.completePendingCommand()).thenReturn(false); 

はMockitoを伝えるために:completePendingCommand()が呼び出されたときに、その後、falseを返します。あなたはのためにそれを行う必要がありますすべてのテスト中のコードが呼び出す方法!

それ以上。以下のようなあなたのコードには多くの問題、:

public SimpleFtpFileImporter() { 
} 

は空にすべきではありません。そこ

public SimpleFtpFileImporter() { 
this(new FtpClient()); 
} 

単純な答え:フィールドは、デフォルトで最終する必要があります(あなたがそれらを最終的にしないために正当な理由がない限り):

private final FTPClient ftpClient; 

、その代わりに、あなたのような何かをやるべきコンパイラはと言っていますが、あなたはそのフィールドを初期化するのを忘れてしまいました!あなたのケースでは、は、そのフィールドを建設時に初期化しないようにする理由はありません。です。それだけで、クラス全体がより複雑でテストするのが難しくなります。

+0

もっとコードを追加しました。アップデート –

+0

をご覧ください。 – GhostCat

+0

これらのヒントをお寄せいただきありがとうございます。しかし、1.外部からのテストの隔離はどうですか? 2. 'new FtpClient()'を呼び出して作成したFtpClientオブジェクトと 'Mockito.mock(FtpClient.class)'を呼び出して作成したFtpClientオブジェクトの違いは何ですか?私は前者のものではnullにならないのですが、後者でnullになるのはなぜですか? Mockitoは、FtpClientのコンストラクタがオブジェクトをインスタンス化する方法を制御できますか? –

関連する問題