2013-11-26 10 views
5

ラムダの可視性を持たずにラムダ内でロジックをスタブ/モックする方法が見つかったのでしょうか?それはあなたが求めているものだが、あなたは別のクラスにラムダすなわちからラムダを抽出したり、であるとして、パラメータとして渡すことができればJava 8 Lambdaのユニットテスト

public List<Item> processFile(String fileName) { 
    // do some magic.. 
    Function<String, List<String>> reader = (fileName) -> { 
     List<String> items = new ArrayList<>(); 
     try (BufferedReader br = new BufferedReader(new FileReader(fileName))) { 
      String output; 
      while ((output = br.readLine()) != null) { 
      items.add(output); 
      } 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
    return items; 
    }; 

    List<String> lines = reader.apply("file.csv"); 
    // do some more magic.. 
} 
+0

ラムダは、何かのようなコンテキストを読み込むことができます(内部にスタブを置くことを望む場合)。変数をキャプチャしないラムダはたぶんシングルトンになることに注意してください。 – aepurniet

答えて

9

私はルールがあると言うでしょうが、そのラムダ式を使用すると、それはおそらくあまりにも複雑だと、それのビットをモックする必要性を感じるように複雑である場合。それは一緒に構成された小さな断片に分割されるべきです、あるいは、おそらく、モデルは合成にもっと従順になるように調整する必要があります。

私はAndrey Chaschev's answerということで、依存関係をパラメータ化することは良いことであり、状況によってはおそらく適用可能であると言います。だから+1。しかし、この上

public List<Item> processFile(
    String fileName, 
    Function<String, BufferedReader> toReader, 
    Function<BufferedReader, List<String>> toStringList, 
    Function<List<String>, List<Item>> toItemList) 
{ 
    List<String> lines = null; 
    try (BufferedReader br = toReader.apply(fileName)) { 
     lines = toStringList.apply(br); 
    } catch (IOException ioe) { /* ... */ } 

    return toItemList.apply(lines); 
} 

カップルの観測:ワンこのプロセスを継続し、小片に処理を打破するため、などの可能性があります。まず、さまざまなラムダが厄介なものを投げるので、これはチェックされ、Functionタイプはその例外をスローするように宣言されていないため、書かれたとおりに動作しません。もう1つは、この関数に渡さなければならないラムダが怪物だということです。これは(あるためチェック例外の)動作しませんが、私はそれを書いた:

void processAnActualFile() { 
    List<Item> items = processFile(
     "file.csv", 
     fname -> new BufferedReader(new FileReader(fname)), 
       // ERROR: uncaught IOException 
     br -> { 
      List<String> result = new ArrayList<>(); 
      String line; 
      while ((line = br.readLine()) != null) { 
       result.add(line); 
      } 
      return result; 
     },  // ERROR: uncaught IOException 
     stringList -> { 
      List<Item> result = new ArrayList<>(); 
      for (String line : stringList) { 
       result.add(new Item(line)); 
      } 
      return result; 
     }); 
} 

うわを!私は新しいコードのにおいを発見したと思う:

ラムダの中にforループまたはwhileループを書く必要があるなら、何か間違っている。

ここではいくつかのことが行われています。第1に、I/Oライブラリは実際に密接に結合された異なる実装(InputStream,ReaderBufferedReader)で構成されています。それらを分解しようとするのは本当に有用ではありません。確かに、図書館は進化しており、あなたのために多忙な仕事を処理するいくつかの便利なユーティリティ(NIO Files.readAllLinesなど)があります。

より重要な点は、値の集約(リスト)を渡してこれらの関数を作成する設計関数が実際には間違った方法であることです。それはすべての関数をその内部にループを書く必要があります。我々が本当にやりたいことは、それぞれが単一の値で動作する関数を書くことです。それから、Java 8の新しいStreamsライブラリが私たちの集計を処理するようにします。

List<String>List<Item>に変換するコメント "do some more magic"に記述されているコードからここで抽出するキー機能です。

class Item { 
    static Item fromString(String s) { 
     // do a little bit of magic 
    } 
} 

あなたがこれをしたら、あなたはストリームとNIOライブラリはあなたのための作業の束をやらせることができます:

我々はこのように、 ItemStringを変換計算を抽出したいです
public List<Item> processFile(String fileName) { 
    try (Stream<String> lines = Files.lines(Paths.get(fileName))) { 
     return lines.map(Item::fromString) 
        .collect(Collectors.toList()); 
    } catch (IOException ioe) { 
     ioe.printStackTrace(); 
     return Collections.emptyList(); 
    } 
} 

(この短い方法の半分以上はIOExceptionを処理するためのものです。)

ユニットテストをしたいのであれば、本当にテストする必要があるのは、ちょっとした魔法です。だから、このように、異なるストリームパイプラインにそれをラップ:。。

void testItemCreation() { 
    List<Item> result = 
     Arrays.asList("first", "second", "third") 
       .stream() 
       .map(Item::fromString) 
       .collect(Collectors.toList()); 
    // make assertions over result 
} 

(実際には、これでもかなり右ではありませんあなたは、単一のItemに単一の行を変換するためのユニットテストを書きたいと思います。しかし多分あなたは、あなたが項目のリストこのように変換し、リスト中の得られた項目の関係の上に世界的な主張を作ると思いますので、どこかにいくつかのテストデータを持っている。)


私はさまよってきましたラムダを分割する方法についてのあなたの元の質問からかなり離れています。自分自身を甘やかすために私を許してください。

Java I/Oライブラリは非常に扱いにくいため、元の例のlambdaは非常に不幸です。この例を1ライナーにする新しいAPIがNIOライブラリにあります。

ここでも、集合体を処理する関数を作成するのではなく、個々の値を処理する関数を作成し、ストリームが集約を処理するようにします。このようにして、複雑なラムダのビットを模擬してテストするのではなく、ストリームパイプラインをさまざまな方法で接続することでテストできます。

3

は私はわかりません。例では、私はリーダーの作成モックの下:

public static void processFile(String fileName, Function<String, BufferedReader> readerSupplier) { 
    // do some magic.. 
    Function<String, List<String>> reader = (name) -> { 
     List<String> items = new ArrayList<>(); 
     try(BufferedReader br = readerSupplier.apply(name)){ 
      String output; 
      while ((output = br.readLine()) != null) { 
       items.add(output); 
      } 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 

     return items; 
    }; 

    List<String> lines = reader.apply(fileName); 
    // do some more magic.. 
} 

public static void main(String[] args) { 
    // mocked call 
    processFile("file.csv", name -> new BufferedReader(new StringReader("line1\nline2\n"))); 

    //original call 
    processFile("1.csv", name -> { 
     try { 
      return new BufferedReader(new FileReader(name)); 
     } catch (FileNotFoundException e) { 
      throw new RuntimeException(e); 
     } 
    }); 
} 
+0

+1は、関数としてパラメータとして渡す潜在的に有用な手法です。 –

関連する問題