2012-01-10 3 views
7

코드 범위를 검토하면서 많은 유닛 테스트가 finally 블록에서 열려있는 InputStream을 닫으려고 시도하는 블록을 확인하지 못했습니다.유닛 테스트가 Java 6에서 최종 블록으로 테스트되었습니다.

한 예 발췌은 다음과 같습니다

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

최종적으로 JUnit4를 사용하여 블록 내에서 모든 것을 확인하는 적절한 솔루션이 있습니까?

나는 최대의 생산성을 염두에두고 100 %의 코드 적용 범위를 달성 할 수 없다는 것을 알고 있습니다. 그러나이 빨간 선은 보고서에서 일종의 안경점입니다.

답변

6

우선으로 검증되지 않은 코드 (아마도 중복)을 줄일 수있는 IOUtils.closeQuietly()을 사용하는 것이 좋습니다. "오른쪽"방법은 BufferedInputStream의 생성을 다른 클래스로 구체화하고 모의 (mock)를 주입하는 것입니다. 모의이 있으면 적절한 close() 메서드가 호출되었는지 확인할 수 있습니다. fileSystemFileSystem 인터페이스의 인스턴스

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

:

@JeffFoster의 대답은 그러나 나는 (더 코드의 비용으로) 상속을 통해 구성을 추천 할 것입니다, 내가 무엇을 의미하는지에 아주 가까이있다 테스트를 위해 생산 코드 또는 모의 (mock)에 간단한 실제 구현을 주입했습니다.

interface FileSystem { 

    InputStream open(String file); 

} 

파일 열기를 외부화하는 또 다른 이점은 버퍼링을 제거하거나 암호화를 추가하기로 결정한 경우 수정할 위치가 하나뿐임을 확인하는 것입니다. 또는 공장을 만들 - -와 모의의 close() 방법을 던질 후 호출 될 때

//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(); 
+0

동의합니다. 테스트에서 메소드를 오버라이드하는 옵션이 매우 유용하지만, 종종 컴포지션을 선택하는 중간 단계입니다. C#은 그 점에서 PITA입니다. 메서드가 기본적으로 가상이 아니므로 전체적인 방법으로 점프해야한다는 것을 알게되었습니다 (가능한 한 가장 작은 변경으로 작업하기를 원할 때 귀찮습니다). –

+0

정확히 내가 뭘 찾고 있었는지 고마워요 :) 감사합니다 또한 제프 – fyr

5

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 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 void TestSomething() { 
    InputStream foo = mock(InputStream.class); // mock object 
    TestMe testMe = new TestMe() { 
    @Override 
    public InputStream createStream() { 
      return foo; 
    } 
    } 

    testMe.something(); 

    verify(foo.close()); 
} 

가치가 있는지 여부는 다른 질문입니다. 지금은 어려워진다

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

: 모든

0

당신은 조롱 BufferedInputStream를 삽입해야합니다

당신이 (Mockito를 사용하여) 모의 객체로 테스트 코드의 인스턴스를 해당 인터페이스를 갖는 IOException.

또한 여기에 논리가 없기 전까지는 finally 블록을 사용하지 않을 것입니다.

0

필자는 이것이 실제로 테스트 노력에 가치가 있는지 여부를 자문해볼 필요가 있다고 생각합니다. 일부 테스트 중독자는 ~ 100 % 테스트 커버리지를 얻으려는 시도가 줄어들지 않는 경향이 있습니다.이 경우에는 제안 된 솔루션 중 일부가 실제 코드에 대해 개의 복잡성을 추가하여 "테스트 가능"하게 만드는 것처럼 보입니다. 나는 복잡한 테스트 코드로도 괜찮지 만 실제 코드에 복잡성을 추가하는 것만으로 "테스트 가능"하게 만들면 나를 끔찍한 생각으로 보게된다.