요구 사항 또는 방법에 따라 단위 테스트 나누기


16

먼저, 제목에 대한 사과, 나는 그것을 설명하는 가장 쉬운 방법을 생각할 수 없었다!

단위 테스트를 작성하려는 방법이 있습니다. 메소드의 구현, 테스트 방법에 대해서만 논의하고 싶지 않기 때문에 상당히 일반적으로 사용하겠습니다. 방법은 다음과 같습니다

public void HandleItem(item a)
{         
     CreateNewItem();
     UpdateStatusOnPreviousItem();
     SetNextRunDate();
}

따라서이 클래스에는 하나의 공용 메소드가 있으며이 메소드는 일부 개인 메소드를 호출하여 논리를 수행합니다.

따라서 단위 테스트를 작성할 때 세 가지 작업이 모두 완료되었는지 확인하고 싶습니다. 그들은 모두 같은 실행에서 부름을 받았으므로 하나의 테스트로 할 수 있다고 생각했습니다.

public void GivenItem_WhenRun_Thenxxxxx
{
     HandleItem(item);
     // Assert item has been created
     // Assert status has been set on the previous item
     // Assert run date has been set
}

그러나 나는 그것을 세 가지 별도의 테스트로 쓸 수도 있다고 생각했습니다.

public void GivenItem_WhenRun_ThenItemIsCreated()
{
    HandleItem(item);
}

public void GivenItem_WhenRun_ThenStatusIsUpdatedOnPreviousItem()
{
   HandleItem(item);
}

public void GivenItem_WhenRun_ThenRunDateIsSet()
{
     HandleItem(item);
}

나에게 이것은 본질적으로 요구 사항을 나열 할 때 더 좋게 보이지만 세 가지 모두 관련되어 있으며 테스트 된 메소드에서 정확히 동일한 작업을 수행해야하므로 동일한 코드를 3 번 ​​실행합니다.

이를 위해 권장되는 접근 방법이 있습니까?

감사

답변:


29

두 가지 방법 사이에는 미묘한 차이가 있습니다. 첫 번째 경우 첫 번째 Assert가 실패하면 다른 두 개는 더 이상 실행되지 않습니다. 두 번째 경우, 세 가지 테스트는 모두 실패하더라도 항상 실행됩니다. 테스트 된 기능의 특성에 따라 다음과 같은 경우에 적합하거나 적합하지 않을 수 있습니다.

  • 하나가 실패하더라도 다른 두 가지가 여전히 실패하지 않을 수 있기 때문에 세 가지 어설 션을 다른 어설 션과 독립적으로 실행하는 것이 합리적이라면 두 번째 방법은 한 번의 실행으로 세 가지 테스트에 대한 전체 테스트 결과를 얻을 수 있다는 이점이 있습니다. 다음 빌드를 수행하기 전에 한 번에 최대 3 개의 오류를 수정할 수 있으므로 빌드 시간이 눈에 띄면 유용 할 수 있습니다.

  • 그러나 첫 번째 테스트 실패가 항상 다른 두 테스트도 실패한다는 것을 암시하는 경우 첫 번째 방법을 사용하는 것이 좋습니다 (사전 테스트를 이미 알고 있다면 테스트를 실행하는 것이 이치에 맞지 않기 때문에). 불합격).


2
+1, 좋은 지적. 빌드 시간이 병목 현상이 될 수 있다는 것은 나에게 발생하지 않았습니다 .
Kilian Foth

1
@KilianFoth : 당신은 C ++에서 자주 일하고 있지 않습니다 :(
Matthieu M.

1
@MatthieuM .: 공정하게, 질문은 "C #"로 태그됩니다
Doc Brown

10

짧은 대답 : 테스트 가 수행 하는 방식 보다 모든 기능을 테스트 하는 것이 훨씬 중요 합니다.

더 긴 대답 : 여전히 이와 동등한 솔루션 중에서 선택하려는 경우 가장 좋은 방법에 대한 보조 기준을 사용할 수 있습니다. 예를 들어

  • 가독성 : 방법이 밀접하게 관련되지 않은 많은 작업을 수행하는 경우 결합 된 테스트를 이해하기 어려울 수 있습니다. 그러나 메소드 자체도 이해하기 어려울 수 있으므로 테스트 대신 메소드를 리팩터링해야합니다!)
  • 효율성 : 메소드를 실행하는 데 시간이 오래 걸리는 경우 시간을 절약하기 위해 세 가지 검사를 모두 결합하는 데 약한 이유 일 수 있습니다.
  • 효율 2 : 프레임 워크의 설정 코드를 실행하는 데 시간이 오래 걸리는 경우 여러 테스트 방법을 피해야하는 약한 이유 일 수도 있습니다. (그러나 이것이 실제로 문제가된다면 yuo는 아마도 테스트 설정을 수정하거나 변경해야합니다. 회귀 테스트는 번개처럼 빨리 실행할 수 없으면 많은 가치를 잃습니다.)

2

여러 개의 어설 션이있는 하나의 메서드 호출을 사용하십시오. 이유는 다음과 같습니다.

HandleItem (a)을 테스트 할 때 메서드가 항목을 올바른 상태로 가져 왔는지 테스트하고 있습니다. "테스트 당 하나의 주장"대신 "테스트 당 하나의 논리적 개념"을 생각하십시오.

질문 : CreateNewItem이 실패하지만 다른 두 가지 방법이 성공하면 HandleItem이 성공적으로 완료되었음을 의미합니까? 나는 추측하지 않습니다.

여러 개의 어설 션 (적절한 메시지 포함)을 사용하면 무엇이 실패했는지 정확히 알 수 있습니다. 일반적으로 여러 번의 어설 션을 피하기 위해 여러 입력 또는 입력 상태에 대해 여러 번 방법을 테스트합니다.

IMO, 이러한 질문은 일반적으로 다른 무언가의 징조입니다. 이것은 HandleItem이 다른 메소드에 위임하는 것처럼 보이기 때문에 실제로 "단위 테스트"할 수있는 것이 아니라는 신호입니다. HandleItem이 다른 메서드를 올바르게 호출하는지 간단히 확인하면 통합 테스트 후보가됩니다 (이 경우 여전히 3 개의 어설 션이 있음).

다른 3 가지 방법을 공개하고 독립적으로 테스트하는 것이 좋습니다. 또는 다른 클래스로 추출 할 수도 있습니다.


0

두 번째 방법을 사용하십시오. 첫 번째 접근 방식을 사용하면 테스트에 실패하면 실패한 세 가지 기능 중 하나 일 수 있으므로 이유를 알 수 없습니다. 두 번째 방법을 사용하면 문제가 발생한 위치를 즉시 알 수 있습니다. 테스트 설정 기능에 중복 코드를 넣을 수 있습니다.


-1

IMHO는이 방법의 세 부분을 개별적으로 테스트하여 코드의 동일한 부분을 두 번 거치지 않고 문제가 발생했을 때 잘못되는 부분을보다 구체적으로 알 수 있습니다.


-2

귀하의 사용 사례에 대해 별도의 테스트 방법을 작성하는 강력한 사례는 없다고 생각합니다. 세 가지 변수 조건 모두에서 결과를 얻으려면 세 가지를 모두 테스트하고 오류를 a로 연결하고 string테스트를 완료 한 후에도 문자열이 여전히 비어 있는지 확인하여 오류를 인쇄 할 수 있습니다 . 그것들을 모두 같은 방법으로 유지함으로써, 조건과 실패 메시지는 방법의 예상 사후 조건을 나중에 분리 될 수있는 세 가지 방법으로 나누지 않고 한 곳에서 문서화합니다.

이것은 단일 테스트에 더 많은 코드가 있다는 것을 의미하지만 문제가되는 사후 조건이 너무 많은 경우 HandleItem어쨌든 내부 메서드를 테스트하기 위해 테스트 메서드를 리팩터링하려고 할 수 있습니다 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.