7

コード カバレッジを確認しているときに、多くの単体テストが、finally ブロックで開いている InputStreams を閉じようとする finally ブロックのチェックに失敗していることに気付きました。

一例の抜粋は次のとおりです。

  try {
      f = new BufferedInputStream(new FileInputStream(source));
      f.read(buffer);
  } finally {
      if (f != null)
          try {
              f.close();
          } catch (IOException ignored) {
          }
      }
  }

JUnit4 を使用して finally ブロック内のすべてをチェックする適切な解決策はありますか?

最大限の生産性を念頭に置きながら、100% のコード カバレッジを達成することは不可能であることを私は知っています。ただし、これらの赤い線は、レポートの中で目印のようなものです。

4

4 に答える 4

6

まず最初に を使用することを検討してIOUtils.closeQuietly()ください。これにより、テストされていないコード (およびおそらく重複) が次のように削減されます。

  try {
      f = new BufferedInputStream(new FileInputStream(source));
      f.read(buffer);
  } finally {
      IOUtils.closeQuietly(f);
  }

今、それは難しくなります。「正しい」方法は、の作成を別のクラスに外部化し、BufferedInputStreamモックを注入することです。モックを作成すると、適切なclose()メソッドが呼び出されたかどうかを確認できます。

@JeffFosterの答えは私が意味するものにかなり近いですが、継承よりも構成をお勧めします (より多くのコードを犠牲にして):

  try {
      f = fileSystem.open(source);
      f.read(buffer);
  } finally {
      IOUtils.closeQuietly(f);
  }

本番コードまたはテスト用のモックに注入された単純な実際の実装を備えたインターフェイスfileSystemのインスタンスはどこにありますか。FileSystem

interface FileSystem {

    InputStream open(String file);

}

ファイルを開くことを外部化するもう 1 つの利点は、バッファリングを削除したり、暗号化を追加したりする場合に、変更する場所が 1 か所だけになることです。

そのインターフェイスを使用して、モックを使用してテスト コードをインスタンス化します (Mockito を使用)。

//given
FileSystem fileSystemMock = mock(FileSystem.class);
InputStream streamMock = mock(InputStream.class);

given(fileSystemMock.open("file.txt")).willReturn(streamMock);

//when
//your code

//then
verify(streamMock).close();
于 2012-01-10T14:37:39.977 に答える
5

コードを少しリファクタリングできます

public class TestMe {
  public void doSomething() {
    try {
      f = new BufferedInputStream(new FileInputStream(source));
      f.read(buffer);
    } finally {
      if (f != null)
      try {
          f.close();
      } catch (IOException ignored) { }
    }
  }
}

このようなものに

public class TestMe {
  public void doSomething() {
    try {
      f = createStream()
      f.read(buffer);
    } finally {
      if (f != null)
      try {
          f.close();
      } catch (IOException ignored) { }
    }
  }

  public InputStream createStream() {
      return new BufferedInputStream(new FileInputStream(source));
  }
}

これで、テストを記述して入力ストリーム クラスをキャプチャし、それが閉じていることを確認できます。(コードは大雑把ですが、大まかなアイデアが得られることを願っています)。

public void TestSomething () {
   InputStream foo = mock(InputStream.class); // mock object
   TestMe testMe = new TestMe() {
     @Override
     public InputStream createStream() {
          return foo;
     } 
   }

   testMe.something();

   verify(foo.close());
}

これが価値があるかどうかは別の問題です!

于 2012-01-10T14:36:51.367 に答える
0

それが本当にテスト努力の価値があるかどうかを自問する必要があると思います。一部のテスト中毒者は、最大100%のテストカバレッジを達成しようとする収穫逓減を見逃す傾向があります。この場合、提案されたソリューションのいくつかは、実際のコードを「テスト可能」にするために、実際のコードをさらに複雑にしているように見えます。複雑なテストコードは問題ありませんが、実際のコードを「テスト可能」にするためだけに複雑さを加えると、ひどい考えになります。

于 2012-01-10T14:45:45.857 に答える
0

モックを注入するBufferedInputStreamか、ファクトリで作成し、モックのclose()メソッドが呼び出されたときにIOException.

さらに、ロジックがなくなるまで、上記の finally ブロックは行いません。

于 2012-01-10T14:38:41.100 に答える