문제

zip 파일이 주어지면 다음과 같은 구성 요소를 작성하고 있습니다.

  1. 파일을 압축 해제하십시오.
  2. 압축 된 파일 중 특정 DLL을 찾으십시오.
  3. 반사를 통해 해당 DLL을로드하고 메소드를 호출하십시오.

이 구성 요소를 테스트하고 싶습니다.

파일 시스템을 직접 처리하는 코드를 작성하려는 유혹이 있습니다.

void DoIt()
{
   Zip.Unzip(theZipFile, "C:\\foo\\Unzipped");
   System.IO.File myDll = File.Open("C:\\foo\\Unzipped\\SuperSecret.bar");
   myDll.InvokeSomeSpecialMethod();
}

그러나 사람들은 종종 "파일 시스템, 데이터베이스, 네트워크 등에 의존하는 단위 테스트를 작성하지 마십시오"라고 말합니다.

이것을 단위 테스트 친화적 인 방식으로 작성하려면 다음과 같이 보일 것이라고 생각합니다.

void DoIt(IZipper zipper, IFileSystem fileSystem, IDllRunner runner)
{
   string path = zipper.Unzip(theZipFile);
   IFakeFile file = fileSystem.Open(path);
   runner.Run(file);
}

예! 이제 테스트 가능합니다. 테스트 복식 (모의)을 DOIT 방법으로 공급할 수 있습니다. 그러나 어떤 비용으로? 이제 테스트 가능하게 만들기 위해 3 개의 새로운 인터페이스를 정의해야했습니다. 그리고 정확히 무엇을 테스트하고 있습니까? 내 DOIT 기능이 종속성과 올바르게 상호 작용한다고 테스트하고 있습니다. ZIP 파일이 제대로 압축되어 있음을 테스트하지 않습니다.

더 이상 기능을 테스트하는 것 같지 않습니다. 클래스 상호 작용을 테스트하는 것처럼 느껴집니다.

내 질문은 이것입니다: 파일 시스템에 의존하는 것을 단위 테스트하는 올바른 방법은 무엇입니까?

편집하다 .NET을 사용하고 있지만 개념은 Java 또는 기본 코드도 적용 할 수 있습니다.

도움이 되었습니까?

해결책

이것에는 아무런 문제가 없습니다. 단지 단위 테스트 또는 통합 테스트라고하는 문제 일뿐입니다. 파일 시스템과 상호 작용하는 경우 의도하지 않은 부작용이 없도록해야합니다. 구체적으로, 당신이 만든 후에 당신이 만든 임시 파일을 삭제하고 당신이 사용중인 임시 파일과 동일한 파일 이름을 갖는 기존 파일을 실수로 덮어 쓰지 않도록하십시오. 항상 절대 경로가 아닌 상대 경로를 사용하십시오.

또한 좋은 생각이 될 것입니다 chdir() 테스트를 실행하기 전에 임시 디렉토리로 chdir() 나중에 돌아 왔습니다.

다른 팁

예! 이제 테스트 가능합니다. 테스트 복식 (모의)을 DOIT 방법으로 공급할 수 있습니다. 그러나 어떤 비용으로? 이제 테스트 가능하게 만들기 위해 3 개의 새로운 인터페이스를 정의해야했습니다. 그리고 정확히 무엇을 테스트하고 있습니까? 내 DOIT 기능이 종속성과 올바르게 상호 작용한다고 테스트하고 있습니다. ZIP 파일이 제대로 압축되어 있음을 테스트하지 않습니다.

당신은 머리에 못을 박았습니다. 테스트하려는 것은 방법의 논리입니다. 반드시 실제 파일을 해결할 수 있는지 여부는 아닙니다. 파일이 올바르게 압축되어 있는지 여부를 테스트 할 필요가 없습니다 (이 단위 테스트에서) 메소드는 당연한 것으로 간주됩니다. 인터페이스는 하나의 콘크리트 구현에 암시 적 또는 명시 적으로 의존하기보다는 프로그램에 대해 프로그램 할 수있는 추상화를 제공하기 때문에 그 자체로 가치가 있습니다.

귀하의 질문은 개발자를위한 가장 어려운 테스트 부분 중 하나를 드러냅니다.

"도대체 나는 무엇을 테스트합니까?"

예제는 일부 API 호출 만 접착하기 때문에 그다지 흥미롭지 않으므로 단위 테스트를 작성하려면 방법이 호출되었다고 주장합니다. 이와 같은 테스트는 구현 세부 정보를 테스트에 엄격하게 결합합니다. 구현 세부 사항을 변경하면 테스트가 나빠지기 때문에 메소드의 구현 세부 사항을 변경할 때마다 테스트를 변경해야하기 때문에 이것은 나쁘다!

테스트가 잘못되는 것은 실제로 테스트가 전혀없는 것보다 더 나쁩니다.

예에서 :

void DoIt(IZipper zipper, IFileSystem fileSystem, IDllRunner runner)
{
   string path = zipper.Unzip(theZipFile);
   IFakeFile file = fileSystem.Open(path);
   runner.Run(file);
}

모의를 통과 할 수는 있지만 테스트 할 방법에는 논리가 없습니다. 이것에 대한 단위 테스트를 시도한다면 다음과 같은 것처럼 보일 수 있습니다.

// Assuming that zipper, fileSystem, and runner are mocks
void testDoIt()
{
  // mock behavior of the mock objects
  when(zipper.Unzip(any(File.class)).thenReturn("some path");
  when(fileSystem.Open("some path")).thenReturn(mock(IFakeFile.class));

  // run the test
  someObject.DoIt(zipper, fileSystem, runner);

  // verify things were called
  verify(zipper).Unzip(any(File.class));
  verify(fileSystem).Open("some path"));
  verify(runner).Run(file);
}

축하합니다. 기본적으로 귀하의 구현 세부 사항을 복사했습니다. DoIt() 테스트에 대한 방법. 행복한 유지.

테스트를 작성하면 테스트하려고합니다 무엇 그리고 아닙니다 어떻게. 보다 블랙 박스 테스트 이상.

그만큼 무엇 방법의 이름입니다 (또는 적어도해야합니다). 그만큼 어떻게 방법 내부에 사는 작은 구현 세부 사항입니다. 좋은 테스트를 통해 교체 할 수 있습니다 어떻게 깨지지 않고 무엇.

이런 식으로 생각하고 스스로에게 물어보십시오.

"이 방법의 구현 세부 사항을 변경하면 (공공 계약을 변경하지 않고) 내 테스트를 중단 할 것인가?"

대답이 예라면, 당신은 어떻게 그리고 아닙니다 무엇.

파일 시스템 종속성으로 코드 테스트에 대한 구체적인 질문에 답하기 위해 파일에 대해 좀 더 흥미로운 일이 있었고 Base64 인코딩 된 내용을 저장하고 싶다고 가정 해 봅시다. byte[] 파일에. 이를 위해 스트림을 사용하여 코드가 확인하지 않고 올바른 일을하는 것을 테스트 할 수 있습니다. 어떻게 그것은 그것을한다. 한 가지 예는 다음과 같은 것일 수 있습니다 (Java).

interface StreamFactory {
    OutputStream outStream();
    InputStream inStream();
}

class Base64FileWriter {
    public void write(byte[] contents, StreamFactory streamFactory) {
        OutputStream outputStream = streamFactory.outStream();
        outputStream.write(Base64.encodeBase64(contents));
    }
}

@Test
public void save_shouldBase64EncodeContents() {
    OutputStream outputStream = new ByteArrayOutputStream();
    StreamFactory streamFactory = mock(StreamFactory.class);
    when(streamFactory.outStream()).thenReturn(outputStream);

    // Run the method under test
    Base64FileWriter fileWriter = new Base64FileWriter();
    fileWriter.write("Man".getBytes(), streamFactory);

    // Assert we saved the base64 encoded contents
    assertThat(outputStream.toString()).isEqualTo("TWFu");
}

테스트는 a를 사용합니다 ByteArrayOutputStream 그러나 응용 프로그램 (종속성 주입 사용)에서 Real StreamFactory (아마도 filestreamfactory라고 함)는 반환됩니다. FileOutputStream ~에서 outputStream() 그리고 a File.

흥미로운 점은 write 여기서 방법은 Base64가 인코딩 된 내용을 작성하고 있다는 것입니다. 그래서 우리가 테스트 한 것입니다. 당신을 위해 DoIt() 방법, 이것은 더 적절하게 테스트됩니다. 통합 테스트.

나는 단위 테스트를 용이하게하기 위해 존재하는 유형과 개념으로 내 코드를 오염 시키려고합니다. 물론, 그것이 디자인을 깨끗하고 더 좋게 만들면, 나는 종종 그것이 사실이 아니라고 생각합니다.

이것에 대한 나의 테스트는 당신의 단위 테스트가 100% 적용 범위가 아닐 수있는만큼 많은 일을한다는 것입니다. 실제로 10%일 수 있습니다. 요점은, 단위 테스트는 빠르고 외부 종속성이 없어야한다는 것입니다. "이 방법은이 매개 변수에 대해 NULL을 통과 할 때 인수를 haught니다"와 같은 사례를 테스트 할 수 있습니다.

그런 다음 외부 종속성을 가질 수있는 통합 테스트 (자동화되고 동일한 단위 테스트 프레임 워크를 사용하여)를 추가하고 이와 같은 엔드 투 엔드 시나리오를 테스트 할 수 있습니다.

코드 적용 범위를 측정 할 때는 단위 및 통합 테스트를 모두 측정합니다.

파일 시스템을 치는 데 아무런 문제가 없으며 단위 테스트 대신 통합 테스트라고 생각하십시오. 하드 코딩 된 경로를 상대 경로로 바꾸고 TestData 하위 폴더를 만들어 단위 테스트의 ZIP를 포함합니다.

통합 테스트를 실행하는 데 너무 오래 걸리면 빠른 장치 테스트만큼 자주 실행되지 않도록 분리하십시오.

나는 때때로 상호 작용 기반 테스트가 너무 많은 커플 링을 유발할 수 있고 종종 충분한 가치를 제공하지 않는다고 생각합니다. 여기서 파일을 삭제 해제하는 것을 테스트하고 싶습니다. 올바른 방법을 호출하고 있는지 확인하는 것이 아닙니다.

한 가지 방법은 입력 스트림을 취하기 위해 압축 방법을 작성하는 것입니다. 그런 다음 단위 테스트는 BytearRayInputStream을 사용하여 바이트 어레이에서 이러한 입력 스트림을 구성 할 수 있습니다. 바이트 배열의 내용은 단위 테스트 코드에서 일정 할 수 있습니다.

이것은 이론적으로 변경 될 수있는 특정 세부 사항 (파일 시스템)에 따라 통합 테스트가 더 많을 것 같습니다.

OS를 다루는 코드를 자체 모듈 (클래스, 어셈블리, Jar 등으로 추상화합니다. 귀하의 경우 발견 된 경우 특정 DLL을로드하려고하므로 IDLLLOADER 인터페이스 및 DLLLOADER 클래스를 만드십시오. 인터페이스를 사용하여 앱을 DLLLOADER에서 DLL을 획득하고 테스트하도록하십시오. Alf After Ofter Right 이후의 실선 코드에 대한 책임이 없습니까?

"파일 시스템 상호 작용"이 프레임 워크 자체에서 잘 테스트되었다고 가정하고 스트림으로 작업하는 방법을 작성하고 테스트하십시오. Filestream.open이 프레임 워크 제작자에 의해 잘 테스트되므로 FILESTREAM을 열고 방법으로 전달할 수 있습니다.

클래스 상호 작용 및 기능 호출을 테스트해서는 안됩니다. 대신 통합 테스트를 고려해야합니다. 파일로드 작업이 아닌 필요한 결과를 테스트하십시오.

단위 테스트의 경우 프로젝트에 테스트 파일을 포함시키고 (귀 파일 또는 동등한) 단위 테스트에서 상대 경로를 사용하는 것이 좋습니다. "../testdata/testfile".

프로젝트가 유닛 테스트가 작동하는 것보다 올바르게 내보내기/수입되는 한.

다른 사람들이 말했듯이, 첫 번째는 통합 테스트로 괜찮습니다. 두 번째는 기능이 실제로 수행 해야하는 것만 테스트합니다.

그림과 같이 두 번째 예제는 약간 무의미 해 보이지만 기능이 어떤 단계에서 오류에 반응하는지 테스트 할 수있는 기회를 제공합니다. 예제에 오류가 확인되지 않지만 실제 시스템에서는 의존성 주입을 통해 모든 오류에 대한 모든 응답을 테스트 할 수 있습니다. 그렇다면 비용은 그만한 가치가 있었을 것입니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top