개인용 메소드를 테스트해야합니까? [닫은]


348

개인 메소드를 테스트하는 방법에 대한 이 게시물 을 읽었습니다 . 나는 항상 객체 외부에서 호출되는 공용 메소드 만 테스트하는 것이 더 빠르다고 생각하기 때문에 테스트하지 않습니다. 개인 메소드를 테스트합니까? 항상 테스트해야합니까?



"개인 도우미를 테스트해야합니까?" 예. "개인 도우미를 직접 테스트해야합니까?" 일반적으로 공개 인터페이스를 통해 쉽게 테스트 할 수 있다면 왜 직접 테스트해야합니까? 공용 인터페이스를 통해 도우미의 모든 측면을 테스트하는 것이 복잡해지면 구성 요소가 단일 장치로 존재했던 것보다 오래 지속 되었습니까?
Mihai Danila 2019

답변:


328

개인 메소드를 단위 테스트하지 않습니다. 개인용 메소드는 클래스 사용자에게 숨겨져 야하는 구현 세부 사항입니다. 전용 메서드를 테스트하면 캡슐화가 중단됩니다.

개인 메소드가 거대하거나 복잡하거나 자체 테스트가 필요할 정도로 중요하다는 것을 알게되면 다른 클래스에 넣고 공개합니다 ( Method Object ). 그런 다음 이제는 자체 클래스에 존재하는 이전의 개인용이지만 현재 공용 인 방법을 쉽게 테스트 할 수 있습니다.


88
동의하지 않습니다. 이상적으로 함수 코딩을 시작하기 전에 빠른 테스트를 작성하십시오. 일반적인 입력과 출력이 무엇인지 생각해보십시오. 테스트 (몇 초 이상 걸리지 않아야 함)를 작성하고 테스트가 올바르게 완료 될 때까지 코드를 작성하십시오. 개인용 메소드에 대해 이러한 스타일의 작업을 포기할 이유가 없습니다.
Frank

254
전용 메소드에 테스트가 필요하지 않다고 말하는 것은 자동차가 괜찮은 한 괜찮다고 말하는 것과 같으며, 문제가되는 것은 중요하지 않습니다. 그러나 사용자가 아무것도 알지 못하더라도 내부의 일부 케이블이 느슨해지기 시작한다는 것을 아는 것이 좋지 않습니까? 물론, 모든 것을 공개 할 수 있지만 요점이 무엇입니까? 당신은 항상 개인적인 방법을 원할 것입니다.
Frank

37
"비공개 메소드는 클래스 사용자에게 숨겨져 야하는 구현 세부 사항입니다." 그러나 테스트는 실제로 "정규"(런타임) 사용자와 클래스 인터페이스의 동일한면에 있습니까? ;)
mlvljr

34
테스트하고 싶은 것을 다른 클래스로 끌어낼 수있는 위험은 제품을 과도하게 엔지니어링하고 재사용 할 수없는 백만 개의 재사용 가능한 구성 요소를 갖는 오버 헤드로 끝날 수 있다는 것입니다.
occulus

44
코드를 자동차와 비교하는 것은 잘못입니다. 코드는 시간이 지남에 따라 빠지지 않고 영원 합니다. 퍼블릭 인터페이스 테스트가 ' 괜찮아 보인다 '고 판단 하기 만하면 퍼블릭 코드 테스트가 충분하지 않습니다. 이 경우 개별 메소드를 개별적으로 테스트해도 아무리 노력해도 전체 테스트가 완료되지 않습니다. 코드의 내부 작업 지식을 사용하여 올바른 시나리오를 만들기 위해 공개 코드 전체를 철저히 테스트하는 데 집중하십시오.
rustyx

293

테스트의 목적은 무엇입니까?

지금까지 답변의 대부분은 개인 메소드가 퍼블릭 인터페이스가 잘 테스트되고 작동하는 한 중요하지 않은 구현 세부 사항이라고 말합니다. 테스트의 유일한 목적이 공용 인터페이스가 작동하도록 보장하는 것이라면 이것이 맞습니다 .

개인적으로, 코드 테스트의 주된 용도는 향후 코드 변경으로 인해 문제가 발생하지 않도록하고 문제가있는 경우 디버깅 작업을 지원하는 것입니다. 공개 인터페이스 (더 이상 그렇지 않다면!)가 철저히 목적을 달성하는 것처럼 개인 메소드를 테스트하는 것이 그 목적을 달성한다는 것을 알았습니다.

다음을 고려하십시오. 개인 메소드 B를 호출하는 공용 메소드 A가 있습니다. A와 B는 모두 메소드 C를 사용합니다. C는 변경 될 수 있으며 (아마도 공급 업체에 의해) 벤더가 테스트 실패를 시작합니다. 문제가 A의 C 사용, B의 C 사용 또는 두 가지 모두에 있는지 알 수 있도록 비공개 테스트를 수행해도 B에 대한 테스트를하는 것이 유용하지 않습니까?

공용 메서드의 테스트 범위가 불완전한 경우 개인 메서드를 테스트해도 가치가 높아집니다. 이것이 일반적으로 피하고자하는 상황이지만 효율성 단위 테스트는 버그를 찾는 테스트와 해당 테스트의 개발 및 유지 관리 비용에 따라 달라집니다. 경우에 따라 100 % 테스트 범위의 이점이 해당 테스트 비용을 보증하기에 충분하지 않은 것으로 판단되어 공용 인터페이스의 테스트 범위에 차이가 생길 수 있습니다. 그러한 경우, 개인 메소드에 대한 목표가 명확한 테스트는 코드 기반에 매우 효과적 일 수 있습니다.


72
여기서 "미래 코드 변경"이라는 문제는 항상 어떤 클래스의 내부 작업을 리팩토링한다는 의미입니다. 테스트를 작성하면 리팩토링에 장애가되는 경우가 종종 있습니다.
무법자 프로그래머

40
또한 단위 테스트를 지속적으로 변경하면 테스트의 일관성이 모두 떨어지고 단위 테스트 자체에 버그가 발생할 수도 있습니다.
26 개 중 26

6
@ 17 테스트와 구현이 동기식으로 수정되면 (그렇게해야한다.) 문제가 훨씬 적다.
mlvljr


3
@Pacerier 코드 테스트와 지속적인 자동 테스트 프로세스의 차이점도 있습니다. 개인 메소드가 제대로 작동하는지 확인해야하지만, 소프트웨어의 사용 사례의 일부가 아니기 때문에 개인 메소드에 연결하는 테스트가 없어야합니다.
Didier A.

150

나는 그들의 책 Pragmatic Unit Testing 에서 Dave Thomas와 Andy Hunt의 조언을 따르는 경향이 있습니다 .

일반적으로 테스트 목적으로 캡슐화를 중단하고 싶지 않습니다 (또는 엄마가 "개인을 노출시키지 마십시오!"라고 말한 것처럼). 대부분의 경우 공용 메소드를 사용하여 클래스를 테스트 할 수 있어야합니다. 개인 또는 보호 된 액세스 뒤에 숨겨져있는 중요한 기능이있는 경우, 다른 클래스가 나 가려고 애 쓰고 있다는 경고 신호일 수 있습니다.

그러나 때로는 개인적인 방법을 테스트하는 것을 막을 수 없습니다 . 완전히 강력한 프로그램을 작성 하고 있다는 확신을 줄 수 있기 때문 입니다.


9
개인 메소드를 대상으로하는 단위 테스트를 비활성화하는 것이 좋습니다. 그것들은 코드 결합이며 향후 리팩토링 작업에 부담을 주거나 때로는 기능 추가 또는 수정에 방해가 될 수 있습니다. 구현 작업을 확인하는 자동 방법으로 구현할 때 테스트를 작성하는 것이 좋지만 테스트를 회귀로 유지하는 것은 유리하지 않습니다.
Didier A.

61

프로젝트에서 최신 QA 권장 사항을 점점 더 많이 따르면서 개인 기능을 테스트해야한다고 생각합니다.

함수 당 순환 복잡성 이 10 개 이하 입니다.

이제이 정책을 시행 할 때의 부작용은 매우 큰 공공 기능 중 다수가 더 집중되고 이름이 잘 지정된 개인 기능 으로 나뉘어져 있다는 것입니다 .
공공 기능은 여전히 ​​(물론) 있지만 본질적으로 모든 개인 '하위 기능'을 호출하도록 축소되었습니다.

콜 스택을 읽기가 훨씬 쉽기 때문에 실제로 멋지다. '어떻게 도착했는지')

그러나 이제는 이러한 개인 기능을 직접 단위 테스트하는 것이 더 쉬워 보이고 대규모 공용 기능의 테스트는 시나리오를 해결해야하는 일종의 '통합'테스트로 남겨 둡니다.

그냥 내 2 센트.


2
@jop에 반응하기 위해, 개인 함수 (너무 큰 순환 적 복잡한 공용 함수의 분할로 인해 생성 됨)를 다른 클래스로 내보낼 필요가 없다고 생각합니다. 나는 같은 클래스에서 여전히 공공 기능과 밀접하게 결합하고 싶습니다. 그러나 여전히 단위 테스트를 거쳤습니다.
VonC September

2
내 경험에 따르면 개인 메서드는 공용 메서드에서 재사용되는 유틸리티 메서드입니다. 때때로 원래 클래스를 두 개 (또는 세 개의) 더 응집 된 클래스로 나누는 것이 더 편리하며, 이러한 개인 메소드를 자체 클래스에 공개하여 테스트 할 수 있습니다.
jop September

7
필자의 경우, 새로운 개인 함수는 실제로 공용 함수가 나타내는 더 큰 알고리즘의 일부입니다. 이 기능은 유틸리티가 아닌 더 큰 프로세스 단계로 구성된 작은 부분으로 나뉩니다. 그러므로 한 번에 전체 알고리즘을 단위 테스트하는 대신 단위 테스트를 수행해야합니다.
VonC

순환 복잡성에 관심이있는 사람들을 위해 다음 주제에 대한 질문을 추가했습니다 : stackoverflow.com/questions/105852/…
VonC

제목의 오타로 인해 질문의 URL이 변경되었습니다. stackoverflow.com/questions/105852/…
VonC

51

예, 개인 함수는 공개 메소드로 테스트되었지만 TDD (Test Driven Design)에서는 응용 프로그램의 가장 작은 부분을 테스트하는 것이 좋기 때문에 개인 함수를 테스트합니다. 그러나 테스트 단위 클래스에있을 때는 개인 기능에 액세스 할 수 없습니다. 다음은 개인 메소드를 테스트하기 위해 수행하는 작업입니다.

왜 우리는 개인적인 방법을 가지고 있습니까?

개인 함수는 주로 공개 메소드에서 읽을 수있는 코드를 작성하려고하기 때문에 클래스에 존재합니다. 우리는이 클래스의 사용자가 이러한 메소드를 직접 호출하는 것이 아니라 공용 메소드를 통해 호출하기를 원하지 않습니다. 또한 클래스를 확장 할 때 (보호 된 경우) 행동을 변경하지 않기를 원하므로 비공개입니다.

코딩 할 때는 TDD (Test-driven-design)를 사용합니다. 이것은 때때로 우리가 개인적인 기능을 테스트하고 싶어한다는 것을 의미합니다. 비공개 함수는 phpUnit에서 테스트 할 수 없습니다. Test 클래스에서는 비공개 함수이기 때문에 액세스 할 수 없기 때문입니다.

우리는 여기에 3 가지 해결책이 있다고 생각합니다.

1. 공개 방법을 통해 개인을 테스트 할 수 있습니다

장점

  • 간단한 단위 테스트 ( '해킹'필요 없음)

단점

  • 프로그래머는 공개 메소드를 이해해야하지만 개인 메소드 만 테스트하려고합니다.
  • 응용 프로그램의 테스트 가능한 가장 작은 부분을 테스트하지 않습니다

2. 개인이 매우 중요한 경우, 별도의 새 클래스를 만드는 것이 코드 스멜 일 수 있습니다.

장점

  • 이 클래스를 새로운 클래스로 리팩토링 할 수 있습니다. 중요한 경우 다른 클래스도이를 필요로하기 때문에
  • 테스트 가능한 단위는 이제 공개 방법이므로 테스트 가능

단점

  • 클래스가 필요하지 않은 경우 클래스를 작성하고 싶지 않으며 메소드가 시작된 클래스에서만 사용됩니다.
  • 추가 된 오버 헤드로 인한 잠재적 성능 손실

3. 액세스 수정자를 (최종) 보호로 변경하십시오.

장점

  • 응용 프로그램에서 테스트 가능한 가장 작은 부분을 테스트하고 있습니다. 최종 보호 기능을 사용하는 경우이 기능은 무시할 수 없습니다 (개인과 동일).
  • 성능 손실 없음
  • 추가 오버 헤드 없음

단점

  • 보호 된 개인 액세스 권한을 변경하고 있습니다. 이는 자녀가 액세스 할 수 있음을 의미합니다.
  • 테스트 클래스에는 여전히 Mock 클래스가 필요합니다.

class Detective {
  public function investigate() {}
  private function sleepWithSuspect($suspect) {}
}
Altered version:
class Detective {
  public function investigate() {}
  final protected function sleepWithSuspect($suspect) {}
}
In Test class:
class Mock_Detective extends Detective {

  public test_sleepWithSuspect($suspect) 
  {
    //this is now accessible, but still not overridable!
    $this->sleepWithSuspect($suspect);
  }
}

따라서 테스트 단위는 이제 test_sleepWithSuspect를 호출하여 이전 개인 함수를 테스트 할 수 있습니다.


eddy147, 나는 모의를 통해 보호 방법을 테스트하는 개념을 정말로 좋아한다. 감사!!!!
Theodore R. Smith

15
TDD에 대한 원래 설명에서 단위 테스트에서 단위 는 메소드 / 함수가 아닌 클래스 임을 지적하고 싶습니다 . 따라서 "응용 프로그램의 가장 작은 부분 테스트"를 언급 할 때 가장 작은 테스트 가능한 부분을 방법으로 참조하는 것은 잘못 입니다. 해당 논리를 사용하면 전체 코드 블록 대신 한 줄의 코드 만 사용하는 것이 좋습니다.
Matt Quigley

@Matt 작업 단위는 클래스뿐만 아니라 단일 메소드도 가리킬 수 있습니다.
eddy147

4
@ eddy147 유닛 테스트는 유닛을 클래스로 정의한 테스트 중심 개발입니다. 인터넷에서와 마찬가지로 시맨틱은 많은 것을 의미하도록 확장되었습니다 (즉, 두 사람에게 단위 테스트와 통합 테스트의 차이점이 무엇인지 물어 보면 7 개의 답을 얻습니다). TDD는 단일 책임을 포함하고 클래스의주기적인 복잡성이 없어야하는 단일 책임 (Single Responsibility)을 포함하여 SOLID 원칙을 사용하여 소프트웨어를 작성하는 수단으로 사용되었습니다. 개인 메서드는 캡슐화되어 해당 단위 테스트가 없습니다.
Matt Quigley

"코드를 작성할 때 TDD (Test-driven-design)를 사용합니다. 이는 때때로 우리가 비공개 기능으로 테스트하려는 기능을 우연히 발견한다는 것을 의미합니다." 이 진술에 동의하지 않습니다. 자세한 내용은 아래 답변을 참조하십시오. TDD는 개인 메소드를 테스트해야한다는 의미는 아닙니다. 개인 메소드를 테스트하도록 선택할 수 있습니다. 이것이 선택이지만 TDD가 그렇게하지 않습니다.
Matt Messersmith

41

몇 가지 이유로 개인 기능 테스트를 좋아하지 않습니다. 다음과 같습니다 (TLDR 사람들의 주요 요점).

  1. 일반적으로 클래스의 개인 메서드를 테스트하려는 경우 디자인 냄새입니다.
  2. 공용 인터페이스를 통해 테스트 할 수 있습니다 (클라이언트가 호출 / 사용하는 방식이므로 테스트하려는 방법). 개인 메소드에 대한 모든 통과 테스트에서 녹색 표시등을 보면서 잘못된 보안 감각을 얻을 수 있습니다. 공용 인터페이스를 통해 개인 기능에 대한 최첨단 사례를 테스트하는 것이 훨씬 좋습니다.
  3. 개인 방법을 테스트하면 심각한 테스트 복제 (유사하게 보이는 느낌)가 발생할 수 있습니다. 필요한 것보다 많은 테스트가 중단 될 수 있기 때문에 요구 사항이 변경 될 때 중요한 결과를 초래합니다. 또한 테스트 스위트로 인해 리팩토링하기 어려운 위치에 배치 할 수 있습니다. 테스트 스위트는 안전하게 재 설계하고 리팩토링 할 수 있도록 도와주기 때문에 가장 아이러니합니다.

구체적인 예를 통해 이들 각각을 설명하겠습니다. 2)와 3)은 다소 복잡하게 연결되어 있으므로 개인 예제를 테스트해서는 안되는 별도의 이유를 고려하지만 예제는 비슷합니다.

개인 메소드를 테스트하는 것이 적절한 경우가 있습니다. 위에 나열된 단점을 인식하는 것이 중요합니다. 나중에 자세히 설명하겠습니다.

또한 TDD가 결국 개인 메소드를 테스트하는 데 유효한 변명이 아닌 이유에 대해서도 설명합니다.

나쁜 디자인에서 길을 리팩토링

내가 본 가장 일반적인 (반) 패터 중 하나는 Michael Feathers"Iceberg"클래스라고 부르는 것입니다 (Michael Feathers가 누구인지 모르는 경우 "레거시 코드로 효과적으로 작업하기"). 전문 소프트웨어 엔지니어 / 개발자인지 아는 사람). 이 문제를 일으키는 다른 (반) 패턴이 있지만, 이것이 내가 우연히 발견 한 가장 일반적인 패턴입니다. "Iceberg"클래스에는 하나의 공용 메소드가 있고 나머지는 개인 메소드입니다 (따라서 개인 메소드를 테스트하려는 이유가 있습니다). 일반적으로 고독한 공용 메소드가 있기 때문에 "Iceberg"클래스라고하지만 나머지 기능은 전용 메소드의 형태로 수중에 숨겨져 있습니다.

규칙 평가자

예를 들어, GetNextToken()문자열을 연속적으로 호출하여 예상 결과를 반환하는지 확인 하여 테스트 할 수 있습니다. 이와 같은 함수는 테스트를 보증합니다. 특히 토큰 화 규칙이 복잡한 경우에는 그 동작이 쉽지 않습니다. 그것이 그렇게 복잡한 것은 아니라고 가정하고 공간으로 구분 된 토큰을 묶고 싶습니다. 따라서 테스트를 작성하면 다음과 같이 보일 수 있습니다 (일부 언어에 관계없는 의사 코드, 아이디어는 분명합니다).

TEST_THAT(RuleEvaluator, canParseSpaceDelimtedTokens)
{
    input_string = "1 2 test bar"
    re = RuleEvaluator(input_string);

    ASSERT re.GetNextToken() IS "1";
    ASSERT re.GetNextToken() IS "2";
    ASSERT re.GetNextToken() IS "test";
    ASSERT re.GetNextToken() IS "bar";
    ASSERT re.HasMoreTokens() IS FALSE;
}

글쎄, 그것은 실제로 꽤 좋아 보인다. 변경시이 동작을 유지하고 싶습니다. 그러나 GetNextToken()A는 개인 기능! 그래서 우리는 컴파일조차 할 수 없기 때문에 이것을 테스트 할 수 없습니다 (파이썬과 같은 스크립팅 언어와는 달리 실제로 공개 / 개인을 강제하는 언어를 사용한다고 가정). 그러나 RuleEvaluator단일 책임 원칙 (Single Responsibility Principle)을 따르도록 수업을 바꾸는 것은 어떻습니까? 예를 들어, 파서, 토크 나이저 및 평가자가 하나의 클래스에 걸린 것 같습니다. 그러한 책임을 분리하는 것이 낫지 않습니까? 게다가 Tokenizer클래스 를 만들면 공개 메소드는 HasMoreTokens()and GetNextTokens()입니다. RuleEvaluator클래스는있을 수 있습니다Tokenizer멤버로서의 객체. 이제 Tokenizer클래스 대신 클래스를 테스트하는 것을 제외하고는 위와 동일한 테스트를 유지할 수 있습니다 RuleEvaluator.

UML에서 다음과 같이 보일 수 있습니다.

규칙 평가자가 리팩토링

이 새로운 디자인은 모듈성을 향상 시키므로 잠재적으로 시스템의 다른 부분에서 이러한 클래스를 재사용 할 수 있습니다 (할 수 없었기 전에 개인 메소드는 정의에 의해 재사용 할 수 없음). 이는 이해도 / 지역성을 높이고 RuleEvaluator를 분류 할 때의 주요 이점입니다.

GetNextToken()메소드는 Tokenizer클래스에서 공개 되기 때문에 이번에는 실제로 컴파일한다는 점을 제외하면 테스트는 매우 유사하게 보입니다 .

TEST_THAT(Tokenizer, canParseSpaceDelimtedTokens)
{
    input_string = "1 2 test bar"
    tokenizer = Tokenizer(input_string);

    ASSERT tokenizer.GetNextToken() IS "1";
    ASSERT tokenizer.GetNextToken() IS "2";
    ASSERT tokenizer.GetNextToken() IS "test";
    ASSERT tokenizer.GetNextToken() IS "bar";
    ASSERT tokenizer.HasMoreTokens() IS FALSE;
}

공용 인터페이스를 통해 개인 구성 요소 테스트 및 테스트 복제 방지

당신이 문제를 더 적은 수의 모듈 식 구성 요소로 나눌 수 있다고 생각하지 않더라도 ( 공식적으로 시도 하면 95 % 할 수 있음) 공용 인터페이스를 통해 개인 기능을 테스트 할 수 있습니다. 많은 경우 개인 구성원은 공용 인터페이스를 통해 테스트되므로 테스트 할 가치가 없습니다. 내가 본 것은 매우 유사한 테스트이지만 두 가지 다른 기능 / 방법을 테스트하는 것입니다. 결국 요구 사항이 변경 될 때 (그리고 항상 수행되는 경우) 이제는 1 대신에 2 번의 테스트가 끊어졌습니다. 실제로 모든 개인 방법을 테스트 한 경우 1 아닌 10 번의 테스트가 더 많이 진행될 수 있습니다. , 개인 기능 테스트 (FRIEND_TEST공개 인터페이스를 통해 테스트 할 수있는 공개 또는 리플렉션 사용) 테스트 복제가 발생할 수 있습니다 . 테스트 스위트가 속도를 늦추는 것보다 아무것도 아프지 않기 때문에 당신은 정말로 이것을 원하지 않습니다. 개발 시간을 줄이고 유지 보수 비용을 줄여야합니다! 공용 인터페이스를 통해 테스트 된 개인 메소드를 테스트하는 경우 테스트 스위트는 그 반대의 경우를 잘 수행 할 수 있으며 유지 보수 비용을 적극적으로 증가시키고 개발 시간을 늘릴 수 있습니다. 개인 기능을 공개하거나 유사 FRIEND_TEST및 / 또는 리플렉션 을 사용하는 경우 일반적으로 장기적으로 후회하게됩니다.

Tokenizer클래스 의 다음 가능한 구현을 고려하십시오 .

여기에 이미지 설명을 입력하십시오

하자 말 SplitUpByDelimiter()배열의 각 요소는 토큰이되도록 배열을 반환 할 책임이있다. 또한 GetNextToken()이 벡터에 대한 반복 자라고 가정 해 봅시다 . 따라서 공개 테스트는 다음과 같습니다.

TEST_THAT(Tokenizer, canParseSpaceDelimtedTokens)
{
    input_string = "1 2 test bar"
    tokenizer = Tokenizer(input_string);

    ASSERT tokenizer.GetNextToken() IS "1";
    ASSERT tokenizer.GetNextToken() IS "2";
    ASSERT tokenizer.GetNextToken() IS "test";
    ASSERT tokenizer.GetNextToken() IS "bar";
    ASSERT tokenizer.HasMoreTokens() IS false;
}

Michael Feather가 모색 도구 라고 부르는 것처럼 가정 해 봅시다 . 다른 사람의 개인 부품을 만질 수있는 도구입니다. 예를 들어 FRIEND_TESTgoogletest 또는 언어가 지원하는 경우 반영입니다.

TEST_THAT(TokenizerTest, canGenerateSpaceDelimtedTokens)
{
    input_string = "1 2 test bar"
    tokenizer = Tokenizer(input_string);
    result_array = tokenizer.SplitUpByDelimiter(" ");

    ASSERT result.size() IS 4;
    ASSERT result[0] IS "1";
    ASSERT result[1] IS "2";
    ASSERT result[2] IS "test";
    ASSERT result[3] IS "bar";
}

이제 요구 사항이 바뀌고 토큰 화가 훨씬 더 복잡해 진다고 가정 해 봅시다. 간단한 문자열 구분 기호로 충분하지 않다고 결정 Delimiter하고 작업을 처리 하는 클래스가 필요합니다 . 당연히 하나의 테스트가 중단 될 것으로 예상하지만 개인 기능을 테스트하면 통증이 커집니다.

개인 테스트 방법은 언제 적절한가요?

소프트웨어에는 "하나의 크기가 모두 맞는"것은 없습니다. 때때로 "규칙을 어기는 것"(괜찮은)이 좋습니다. 가능한 경우 개인 기능을 테스트하지 않는 것이 좋습니다. 괜찮다고 생각되는 두 가지 주요 상황이 있습니다.

  1. 나는 레거시 시스템 (광활한 Michael Feathers 팬이기 때문에)과 광범위하게 일해 왔으며 때로는 개인 기능을 테스트하는 것이 가장 안전하다고 말할 수 있습니다. "특성 테스트"를 기준으로 가져 오는 데 특히 도움이 될 수 있습니다.

  2. 당신은 서두르고 있으며 지금 여기에서 가능한 가장 빠른 일을해야합니다. 장기적으로는 개인 메소드를 테스트하고 싶지 않습니다. 그러나 설계 문제를 해결하기 위해 리팩토링하는 데 보통 시간이 걸린다고 말하겠습니다. 때로는 일주일 안에 배송해야합니다. 괜찮습니다. 작업을 완료하는 가장 빠르고 안정적인 방법이라고 생각하는 경우 모색 도구를 사용하여 빠르고 더러운 작업을 수행하고 개인 방법을 테스트하십시오. 그러나 당신이 한 일이 장기적으로 차선책이라는 것을 이해하고 다시 돌아 오는 것을 고려하십시오 (또는 잊어 버렸지 만 나중에 볼 경우 수정하십시오).

다른 상황에서는 괜찮을 것입니다. 괜찮다고 생각하고 정당성이 충분하다면 그렇게하십시오. 아무도 당신을 막고 있지 않습니다. 잠재적 인 비용에주의하십시오.

TDD 실례

제쳐두고, 나는 개인 메소드 테스트에 대한 변명으로 TDD를 사용하는 사람들을 정말로 좋아하지 않습니다. 나는 TDD를 연습하지만 TDD가 당신에게 이것을 강요한다고 생각하지 않습니다. 먼저 공용 인터페이스 용 테스트를 작성한 다음 해당 인터페이스를 만족시키는 코드를 작성할 수 있습니다. 때로는 공용 인터페이스에 대한 테스트를 작성하고 하나 또는 두 개의 더 작은 개인용 메소드를 작성하여이를 만족시킬 것입니다 (그러나 개인용 메소드를 직접 테스트하지는 않지만 작동하거나 공개 테스트가 실패한다는 것을 알고 있습니다 ). 해당 개인 메소드의 엣지 케이스를 테스트 해야하는 경우 공용 인터페이스를 통해 테스트를 수행하는 전체 테스트를 작성합니다.엣지 케이스를 치는 방법을 알 수 없다면 이는 각각 고유 한 공개 방법으로 작은 구성 요소를 리팩토링해야한다는 강력한 신호입니다. 개인 함수가 너무 많은 일을하고 클래스의 범위를 벗어났다는 신호 입니다.

또한 때때로 나는 씹기에는 너무 물린 테스트를 작성한다는 것을 알게 되었기 때문에 "나는 API를 더 사용할 때 나중에 다시 테스트로 돌아갈 것"이라고 생각합니다. 의견을 말하고 내 마음 뒤에 보관하십시오.) 이것은 내가 만난 많은 개발자들이 TDD를 희생양으로 사용하여 개인 기능에 대한 테스트를 작성하는 곳입니다. "아, 다른 테스트가 필요하지만이 테스트를 작성하려면 이러한 개인 메소드가 필요합니다. 따라서 테스트를 작성하지 않고는 프로덕션 코드를 작성할 수 없으므로 테스트를 작성해야합니다. 개인적인 방법으로 그러나 그들이 실제로해야 할 일은 현재 클래스에 많은 개인 메소드를 추가 / 테스트하는 대신 더 작고 재사용 가능한 구성 요소로 리팩토링하는 것입니다.

노트 :

얼마 전 GoogleTest를 사용하여 개인 메소드를 테스트 하는 것과 비슷한 질문에 대답했습니다 . 나는 대부분 더 많은 언어에 대해 그 대답을 수정했습니다.

추신 : Michael Feathers의 빙산 강의 및 모색 도구에 대한 관련 강의는 다음과 같습니다. https://www.youtube.com/watch?v=4cVZvoFGJTU


"시스템의 다른 부분에서 잠재적으로 이러한 클래스를 재사용 할 수있다"는 장점이 있습니다. 때때로 함수를 비공개로 표시 한 이유는 다른 부분에서 사용 하지 않기 때문 입니다. 시스템. 이것은 언어 고유의 문제입니다. 이상적으로는 "모듈"전용이지만, 언어에서 지원하지 않는 경우 (예 : PHP) 내 클래스는 단위가 아닌 모듈을 나타냅니다. 전용 메서드는 재사용 가능한 코드입니다. 자체 계약이 있지만 해당 클래스 내에서만 재사용해야합니다.
IMSoP

나는 당신이 말하는 것을 이해하지만, 파이썬 커뮤니티가 그 문제를 처리하는 방식을 좋아합니다. 문제가있는 "private"멤버의 이름을 Leading _으로 지정하면 "이봐, 이건 'private"입니다. 사용할 수는 있지만 완전 공개는 재사용 할 수 있도록 설계되지 않았으므로 실제로 사용하는 경우에만 사용해야합니다. 뭘하는지 알아 " 어떤 언어로든 동일한 접근 방식을 사용할 수 있습니다. 해당 구성원을 공개로 설정하지만 주요 내용으로 표시하십시오 _. 또는 이러한 기능은 실제로 개인용이어야하며 공용 인터페이스를 통해 테스트해야합니다 (자세한 내용은 답변 참조). 경우에 따라서는 일반적인 규칙이 없습니다
Matt Messersmith

26

객체의 공개 인터페이스를 테스트하는 것이 가장 좋습니다. 외부 세계의 관점에서 볼 때 공용 인터페이스의 동작 만 중요하며 이는 유닛 테스트를 향한 것입니다.

객체에 대해 작성된 견고한 단위 테스트가 있으면 인터페이스 뒤의 구현이 변경되어 다시 돌아가서 테스트를 변경하지 않아도됩니다. 이 상황에서는 단위 테스트의 일관성을 망쳤습니다.


21

공개 메소드를 호출하여 개인 메소드를 테스트하지 않으면 어떻게됩니까? 보호되지 않거나 친구가 아닌 비공개로 이야기하고 있습니다.


3
감사합니다. 이것은 놀랍게도 과소 평가 된 의견이며, 작성된 이후 거의 8 년이 지난 후에도 여전히 관련성이 있습니다.
Sauronlord

1
동일한 추론으로 소프트웨어의 모든 기능이 어떻게 든 실행될 수 있기 때문에 사용자 인터페이스 (시스템 레벨 테스트)에서만 소프트웨어를 테스트한다고 주장 할 수 있습니다.
Dirk Herrmann

18

개인 메소드가 잘 정의 된 경우 (즉, 테스트 가능하고 시간이 지남에 따라 변경되지 않는 기능이있는 경우) yes입니다. 나는 의미가있는 곳에서 테스트 할 수있는 모든 것을 테스트합니다.

예를 들어, 암호화 라이브러리는 한 번에 8 바이트 만 암호화하는 개인 방법으로 블록 암호화를 수행한다는 사실을 숨길 수 있습니다. 단위 테스트를 작성하려고합니다. 숨겨져 있어도 변경하려는 것이 아니며 향후 성능 향상으로 인해 중단되는 경우 그것이 개인 기능이 아니라 개인 기능이라는 것을 알고 싶습니다. 공공 기능 중 하나가 고장났습니다.

나중에 디버깅 속도가 빨라집니다.

-아담


1
이 경우 해당 개인 메소드를 다른 클래스로 이동 한 다음 공개 또는 공개 정적으로 만드는 것이 합리적이지 않습니까?
무법자 프로그래머

+1 개인 멤버 함수를 테스트하지 않고 공개 인터페이스 테스트에 실패하면 어떤 것이 무엇인지 전혀 몰라도 무언가 와 동등한 결과를 얻게됩니다 .
Olumide

12

테스트 기반 (TDD)을 개발하는 경우 개인 방법을 테스트합니다.



4
사실은 아닙니다. 공개 메소드를 테스트하고 테스트가 통과되면 "세척"단계로 인해 공개 메소드의 코드를 개인 메소드로 추출합니다. 비공개 메소드 테스트는 구현 방식을 더 어렵게 변경하기 때문에 나쁜 아이디어입니다. 언젠가 무언가를 수행하는 방법을 변경하려는 경우 메소드를 변경하고 모든 테스트를 실행할 수 있어야하며 새로운 방식으로 수행하는 경우 그들이 통과 해야하는 것이 맞습니다. 이것에 대한 모든 개인 테스트를 변경하고 싶지는 않습니다).
Tesseract

1
@Tesseract, 내가 한 번 이상 당신의 의견을 찬성 할 수 있다면. "... 변경하고 모든 테스트를 실행할 수 있어야하며, 새로운 방식으로 작업을 수행하는 것이 올바른 경우 통과해야합니다."단위 테스트의 주요 이점 중 하나입니다. 자신감있게 리팩토링 할 수 있습니다. 클래스의 내부 개인 작업을 완전히 변경할 수 있으며 (모든 단위 테스트를 다시 작성하지 않고) 모든 (기존 인터페이스) 모든 (공용 인터페이스에서) 단위 테스트가 여전히 통과했기 때문에 아무것도 깨지 않았다는 확신이 있습니다.
Lee

동의 안함, 아래 답변을 확인하십시오
Matt Messersmith

11

저는이 분야의 전문가는 아니지만 단위 테스트는 구현이 아닌 동작을 테스트해야합니다. 전용 메소드는 구현의 일부이므로 IMHO를 테스트하지 않아야합니다.


구현은 어디에서 테스트됩니까? 일부 기능이 캐싱을 사용하는 경우 구현 세부 사항이며 캐싱이 테스트되지 않습니까?
Dirk Herrmann

11

우리는 추론으로 개인 메서드를 테스트합니다. 즉, 최소 95 %의 전체 클래스 테스트 범위를 찾지 만 테스트는 공용 또는 내부 메서드 만 호출합니다. 서비스를 받으려면 발생할 수있는 여러 시나리오에 따라 공개 / 내부에게 여러 번 전화를 걸어야합니다. 이를 통해 테스트중인 코드의 목적에 따라 테스트를 더욱 집중적으로 수행 할 수 있습니다.

연결 한 게시물에 대한 Trumpi의 답변이 가장 좋습니다.


9

내가 생각하는 단위 테스트는 공개 방법을 테스트하기위한 것입니다. 공개 메소드는 개인 메소드를 사용하므로 간접적으로 테스트되고 있습니다.


7

나는 특히 TDD에 손을 대면서이 문제를 잠시 동안 조롱했습니다.

TDD의 경우이 문제를 철저히 해결한다고 생각되는 두 개의 게시물을 보았습니다.

  1. 전용 메소드, TDD 및 테스트 주도 리팩토링 테스트
  2. 테스트 주도 개발이 테스트가 아님

요약해서 말하자면:

  • 테스트 주도 개발 (디자인) 기술을 사용할 때 전용 메소드는 이미 작동하고 테스트 된 코드의 리팩토링 프로세스 중에 만 발생해야합니다.

  • 프로세스의 특성상 철저하게 테스트 된 기능에서 추출 된 간단한 구현 기능은 자체 테스트됩니다 (예 : 간접 테스트 범위).

나에게 코딩의 시작 부분에서 대부분의 메소드는 디자인을 캡슐화 / 설명하기 때문에 더 높은 수준의 함수가 될 것입니다.

따라서 이러한 방법은 공개적이며 테스트하기에 충분할 것입니다.

모든 것이 잘 작동하고 나면 가독성청결을 위해 리팩토링을 고려 하고 있습니다.


6

위에서 인용 한 것처럼 "개인 메소드를 테스트하지 않으면 메소드가 중단되지 않는지 어떻게 알 수 있습니까?"

이것은 중요한 문제입니다. 단위 테스트의 가장 큰 요점 중 하나는 언제, 어떻게, 어떻게 문제가 발생했는지 아는 것입니다. 따라서 상당한 양의 개발 및 QA 노력이 줄어 듭니다. 시험 된 모든 것이 일반인이라면, 수업 내부에 정직한 적용 범위와 묘사가 없습니다.

이 작업을 수행하는 가장 좋은 방법 중 하나는 프로젝트에 테스트 참조를 추가하고 테스트를 개인 메소드와 병렬로 클래스에 배치하는 것입니다. 테스트가 최종 프로젝트에 빌드되지 않도록 적절한 빌드 로직을 넣으십시오.

그런 다음 이러한 방법을 테스트하면 얻을 수있는 모든 이점이 있으며 몇 초 또는 몇 시간 또는 몇 시간으로 문제를 찾을 수 있습니다.

요약하자면, 개인 메소드를 단위 테스트하십시오.


2
동의하지 않습니다. "개인 분석법을 테스트하지 않으면 분석법이 중단되지 않는지 어떻게 알 수 있습니까?" : 내 개인 메소드가 손상되면 해당 개인 메소드에 의존하는 공개 메소드를 테스트하는 테스트가 실패하기 때문에 이것을 알고 있습니다. 공개 메소드를 구현하는 방법에 대한 생각이 바뀔 때마다 테스트를 변경하고 싶지 않습니다. 또한 단위 테스트의 주요 관심사는 어떤 코드 라인에 결함이 있는지를 아는 것이 아니라 개인 메소드를 변경할 때 아무것도 부수 지 않았다는 확신을 가질 수 있다고 생각합니다.
Tesseract

6

해서는 안됩니다 . 개인 메서드에 테스트가 필요한 복잡성이 충분한 경우 다른 클래스에 배치해야합니다. 높은 응집력을 유지 , 클래스는 하나의 목적을 가져야합니다. 클래스 공용 인터페이스이면 충분합니다.


3

당신이 당신의 개인 방법을 테스트하지 않으면 어떻게 깨지지 않는지 어떻게 알 수 있습니까?


19
공개 방법 테스트를 통해 작성합니다.
scubabbl

3
이러한 개인 메소드는 클래스의 공개 메소드에 의해 호출됩니다. 개인 메소드를 호출하는 공용 메소드를 테스트하십시오.
jop

1
공개 메소드가 올바르게 작동하면 액세스하는 개인 메소드가 올바르게 작동합니다.
26 개 중 26

공개 메소드의 테스트가 실패하면 객체 / 컴포넌트 / 등의 하위 레벨에서 무언가 잘못되었다는 것을 즉시 알 수 있습니다.
Rob

3
그건 정말 그것 (내부 기능을 잘 그리고 당신은 외부에 초점을 맞출 수 있다는 것을 반대로 나) 파산 단지 외부 기능을 내장 함수의 아닌 것을 알고 있지만, 좋은.
Adam Davis

2

언어에 따라 다릅니다. 과거에 C ++에서는 테스트 클래스를 친구 클래스로 선언했습니다. 불행히도, 테스트 클래스에 대해 알고 싶다면 프로덕션 코드가 필요합니다.


5
친구 키워드는 나를 슬프게합니다.
Rob

테스트 클래스가 다른 프로젝트에서 구현 된 경우에는 문제가되지 않습니다. 중요한 것은 프로덕션 코드가 테스트 클래스를 참조하지 않는다는 것입니다.
Olumide

2

개인 메소드가 구현 세부 사항으로 간주되어 테스트 할 필요가 없다는 관점을 이해합니다. 그리고 우리가 물체 밖에서 만 개발해야한다면이 규칙을 고수 할 것입니다. 그러나 우리는 객체 외부에서만 개발하고 공개 메소드 만 호출하는 제한된 개발자입니까? 아니면 실제로 그 객체를 개발하고 있습니까? 우리는 외부 객체를 프로그래밍 할 의무가 없기 때문에 이러한 개인 메소드를 개발중인 새로운 공개 메소드로 호출해야 할 것입니다. 개인적인 방법이 모든 확률에 저항한다는 것을 아는 것이 좋지 않습니까?

일부 사람들이 우리가 그 대상에 다른 공개 방법을 개발하고 있다면 이것이 테스트되어야하고 그것이 전부라고 대답 할 수 있다는 것을 알고 있습니다. 그러나 이것은 객체의 모든 공개 메소드에도 적용됩니다. 웹 앱을 개발할 때 객체의 모든 공개 메소드가 컨트롤러 메소드에서 호출되므로 컨트롤러의 구현 세부 사항으로 간주 될 수 있습니다.

그렇다면 왜 단위 테스트 객체입니까? 실제로는 어렵 기 때문에 기본 코드의 모든 분기를 트리거하는 적절한 입력으로 컨트롤러의 메소드를 테스트하고 있다는 것을 확신하는 것은 불가능하지 않습니다. 다시 말해, 스택 상태가 높을수록 모든 동작을 테스트하기가 더 어려워집니다. 그리고 개인 메소드도 마찬가지입니다.

개인적 방법과 공공 방법의 경계는 시험에있어 심리적 기준입니다. 나에게 더 중요한 기준은 다음과 같습니다.

  • 이 방법은 다른 장소에서 두 번 이상 호출됩니까?
  • 테스트가 필요할 정도로 정교합니까?

1

개인 메소드가 거대하거나 복잡하거나 자체 테스트가 필요할 정도로 중요하다는 것을 알게되면 다른 클래스에 넣고 공개합니다 (Method Object). 그런 다음 이전에는 개인용이지만 이제는 자체 클래스에있는 공개 방법을 쉽게 테스트 할 수 있습니다.


1

나는 단위 테스트의 개념을 이해하지 못하지만 이제 목표가 무엇인지 알고 있습니다.

단위 테스트는 완전한 테스트가 아닙니다 . 따라서 품질 보증 및 수동 테스트를 대체하지 않습니다. 이 측면에서 TDD의 개념은 개인 메소드뿐만 아니라 자원 (특히 제어 할 수없는 자원)을 사용하는 메소드를 포함하여 모든 것을 테스트 할 수 없기 때문에 잘못되었습니다. TDD는 모든 품질을 달성 할 수 없었습니다.

단위 테스트는 피벗 테스트입니다. 임의의 피벗을 표시 하면 피벗 결과가 동일하게 유지됩니다.


1

공개 대 개인은 API가 테스트에서 호출하는 것과 유용한 방법이 아니며 방법 대 클래스도 아닙니다. 대부분의 테스트 가능한 단위는 한 상황에서 볼 수 있지만 다른 상황에서는 숨겨져 있습니다.

중요한 것은 적용 범위와 비용입니다. 프로젝트의 적용 범위 목표 (라인, 분기, 경로, 블록, 방법, 클래스, 동등성 클래스, 유스 케이스 ... 팀이 결정한 모든 것)를 달성하면서 비용을 최소화해야합니다.

따라서 도구를 사용하여 적용 범위를 확보하고 테스트를 설계하여 비용을 최소화하십시오 (단기 및 장기 ).

테스트를 필요 이상으로 비싸지 마십시오. 공개 진입 점 만 테스트하는 것이 가장 저렴한 경우에는 그렇게하십시오. 개인 메소드를 테스트하는 것이 가장 저렴한 경우 그렇게하십시오.

경험이 많을수록 장기 테스트 유지 관리 비용을 피하기 위해 리팩토링이 필요한 시점을 더 잘 예측할 수 있습니다.


0

이 방법이 충분히 / 복잡한 경우에는 보통 "보호"하고 테스트합니다. 일부 방법은 비공개로 유지되며 공개 / 보호 방법에 대한 단위 테스트의 일부로 암시 적으로 테스트됩니다.


1
@VisibleForTesting은 이에 대한 주석입니다. 테스트를 위해 캡슐화를 풀지 않고 dp4j.com을 사용하십시오.
simpatico

0

나는 많은 사람들이 같은 생각을하고있는 것을 본다 : 공공 수준에서의 시험. 품질 관리팀이하는 일이 아닙니까? 입력 및 예상 출력을 테스트합니다. 개발자로서 공개 메소드 만 테스트하는 경우 QA의 업무를 다시 수행하고 "단위 테스트"로 가치를 추가하지 않습니다.


현재 추세는 QA 팀을 줄이거 나 없앤 것입니다. 이 단위 테스트는 엔지니어가 마스터 브랜치에서 코드를 푸시 할 때마다 실행되는 자동 테스트가됩니다. QA를 사용하더라도 자동화 된 테스트만큼 빠르게 전체 애플리케이션을 테스트 할 수있는 방법이 없습니다.
Patrick Desjardins

0

"개인 메소드를 테스트해야합니까?" "때때로"입니다. 일반적으로 클래스의 인터페이스에 대해 테스트해야합니다.

  • 그 이유 중 하나는 기능에 대해 이중 적용 범위가 필요하지 않기 때문입니다.
  • 또 다른 이유는 개인 메소드를 변경하면 오브젝트의 인터페이스가 전혀 변경되지 않은 경우에도 각 메소드를 업데이트해야하기 때문입니다.

예를 들면 다음과 같습니다.

class Thing
  def some_string
    one + two
  end

  private 

  def one
    'aaaa'
  end

  def two
    'bbbb'
  end

end


class RefactoredThing
def some_string
    one + one_a + two + two_b
  end

  private 

  def one
    'aa'
  end

  def one_a
    'aa'
  end

  def two
    'bb'
  end

  def two_b
    'bb'
  end
end

에서 RefactoredThing당신 이제 리팩토링에 대한 업데이트했다이있는 5 개 테스트를 가지고 있지만, 개체의 기능은 정말 변경되지 않았습니다. 따라서 사물이 그보다 복잡하고 다음과 같이 출력 순서를 정의하는 방법이 있다고 가정 해 봅시다.

def some_string_positioner
  if some case
  elsif other case
  elsif other case
  elsif other case
  else one more case
  end
end

이것은 외부 사용자가 실행해서는 안되지만 캡슐화 클래스는 많은 논리를 반복해서 실행하기 위해 무거울 수 있습니다. 이 경우에는 이것을 별도의 클래스로 추출하고 해당 클래스에 인터페이스를 제공하고 테스트하십시오.

마지막으로 주 객체가 매우 무겁고 방법이 매우 작으며 실제로 출력이 올바른지 확인해야한다고 가정 해 봅시다. "이 개인 방법을 테스트해야합니다!"라고 생각하고 있습니다. 무거운 작업을 초기화 매개 변수로 전달하여 객체를 더 가볍게 만들 수 있습니까? 그런 다음 더 가벼운 것을 전달하고 테스트 할 수 있습니다.


0

아니요 프라이빗 메소드를 테스트하지 않아야 합니까? 또한 Mockito와 같은 인기있는 조롱 프레임 워크는 개인 메소드 테스트를 지원하지 않습니다.


0

하나의 요점은

논리의 정확성을 확인하기 위해 테스트하고 개인 메소드가 논리를 전달하는 경우 테스트해야합니다. 그렇지 않습니까? 왜 우리는 그것을 건너 뛸까요?

방법의 가시성을 기반으로 테스트를 작성하는 것은 전혀 관련이 없습니다.

거꾸로

반면, 원래 클래스 외부에서 전용 메소드를 호출하는 것이 주요 문제입니다. 또한 일부 조롱 도구에서 개인 메서드를 조롱하는 데 제한이 있습니다. (예 : Mockito )

Power Mock 과 같은 도구가 있지만이를 지원하는 가 있지만 위험한 작업입니다. 그 이유는이를 달성하기 위해 JVM을 해킹해야하기 때문입니다.

할 수있는 한 가지 해결 방법은 (개인 메서드에 대한 테스트 사례를 작성하려는 경우)

보호 된 개인 메소드를 선언하십시오 . 그러나 여러 상황에서는 편리하지 않을 수 있습니다.


0

퍼블릭 또는 프라이빗 메소드 또는 함수뿐만 아니라 구현 세부 사항에 관한 것입니다. 개인 함수는 구현 세부 사항의 한 측면 일뿐입니다.

결국 단위 테스트는 화이트 박스 테스트 방식입니다. 예를 들어, 지금까지 테스트에서 무시 된 코드의 일부를 식별하기 위해 적용 범위 분석을 사용하는 사람은 구현 세부 정보로 이동합니다.

A) 예, 구현 세부 정보를 테스트해야합니다.

성능상의 이유로 최대 10 개의 요소가있는 경우 개인용 BubbleSort 구현을 사용하고 10 개 이상의 요소가있는 경우 다른 정렬 방식 (예 : 힙 정렬)의 개인 구현을 사용하는 정렬 함수를 생각해보십시오. 공개 API는 정렬 함수의 API입니다. 그러나 테스트 스위트는 실제로 두 가지 정렬 알고리즘이 사용된다는 지식을 더 잘 활용합니다.

이 예제에서는 반드시 공개 API에서 테스트를 수행 할 수 있습니다. 그러나 힙 정렬 알고리즘을 충분히 테스트 할 수 있도록 10 개 이상의 요소로 정렬 함수를 실행하는 여러 테스트 사례가 필요합니다. 이러한 테스트 사례 만 존재한다는 것은 테스트 스위트가 기능의 구현 세부 사항에 연결되어 있음을 나타냅니다.

정렬 함수의 구현 세부 사항이 변경되면 두 정렬 알고리즘 사이의 한계가 이동되거나 힙 정렬이 mergesort 등으로 대체되는 방식으로 인해 기존 테스트가 계속 작동합니다. 그럼에도 불구하고 그들의 가치는 의문의 여지가 있으며 변경된 정렬 기능을 더 잘 테스트하기 위해 재 작업해야 할 것입니다. 다시 말해, 테스트가 공개 API에서 수행되었다는 사실에도 불구하고 유지 관리 노력이있을 것입니다.

B) 구현 세부 사항을 테스트하는 방법

많은 사람들이 개인 기능이나 구현 세부 사항을 테스트해서는 안된다고 주장하는 한 가지 이유는 구현 세부 사항이 변경 될 가능성이 높기 때문입니다. 이러한 변경 가능성은 적어도 인터페이스 뒤에 구현 세부 사항을 숨기는 이유 중 하나입니다.

이제 인터페이스 뒤의 구현에는 내부 인터페이스의 개별 테스트가 옵션 일 수있는 더 큰 개인 부품이 포함되어 있다고 가정하십시오. 어떤 사람들은이 부분들이 비공개 일 때 테스트를해서는 안되며, 공개 된 것으로 바꿔야한다고 주장합니다. 일단 공개되면 해당 코드를 단위 테스트해도 괜찮습니다.

이것은 흥미 롭습니다. 인터페이스는 내부에 있지만 구현 세부 사항이므로 변경 될 수 있습니다. 동일한 인터페이스를 사용하여 공개하면 약간의 마술 변형, 즉 변경 가능성이 적은 인터페이스로 전환됩니다. 분명히이 주장에는 약간의 결함이 있습니다.

그럼에도 불구하고, 이것 뒤에 배후의 진실이있다 : 구현 세부 사항을 테스트 할 때, 특히 내부 인터페이스를 사용하여, 안정적으로 유지 될 수있는 인터페이스를 사용하도록 노력해야한다. 그러나 일부 인터페이스가 안정적인지 여부는 공용인지 개인인지에 따라 결정될 수 없습니다. 전 세계에서 일한 프로젝트에서 공용 인터페이스도 종종 충분히 변경되었으며 많은 개인 인터페이스는 오랫동안 변하지 않았습니다.

여전히 "정문"을 사용하는 것이 좋습니다 ( http://xunitpatterns.com/Principles%20of%20Test%20Automation.html 참조 ). 그러나 "정문 전용"이 아니라 "정문 우선"이라고합니다.

C) 요약

구현 세부 사항도 테스트하십시오. 안정적인 인터페이스 (공용 또는 개인)에 대한 테스트를 선호합니다. 구현 세부 사항이 변경되면 공개 API에 대한 테스트도 수정해야합니다. 사적인 것을 공개적으로 바꾸는 것이 마술의 안정성을 바꾸지는 않습니다.


0

그렇습니다. 가능하면 개인 메소드를 테스트해야합니다. 왜? 불필요한 상태 공간 폭발 을 피하려면테스트 케이스 궁극적으로 동일한 입력에서 동일한 개인 기능을 반복적으로 암시 적으로 테스트합니다. 예를 들어 왜 그런지 설명해 봅시다.

다음과 같이 약간 고안된 예를 고려하십시오. 3 개의 정수를 취하고 3 개의 정수가 모두 소수 인 경우에만 true를 반환하는 함수를 공개적으로 노출한다고 가정합니다. 다음과 같이 구현할 수 있습니다.

public bool allPrime(int a, int b, int c)
{
  return andAll(isPrime(a), isPrime(b), isPrime(c))
}

private bool andAll(bool... boolArray)
{
  foreach (bool b in boolArray)
  {
    if(b == false) return false;
  }
  return true;
}

private bool isPrime(int x){
  //Implementation to go here. Sorry if you were expecting a prime sieve.
}

이제 공공 기능 만 테스트해야하는 엄격한 접근 방식을 취해야한다면 테스트 만 할 수 allPrime있고 그렇지 않을 isPrime수도 andAll있습니다.

테스터, 우리는 각 인수에 다섯 가능성에 관심이있을 수 : < 0, = 0, = 1, prime > 1, not prime > 1. 그러나 철저하게, 우리는 또한 모든 논증의 조합이 어떻게 함께 작용 하는지를보아야합니다. 따라서 5*5*5직감에 따라 = 125 테스트 사례입니다.이 기능을 철저히 테스트해야합니다.

반면에 개인 기능을 테스트 할 수 있다면 더 적은 테스트 사례로 많은 근거를 다룰 수있었습니다. isPrime이전 직관과 동일한 수준으로 테스트 하려면 5 개의 테스트 사례 만 있으면됩니다 . 그리고 Daniel Jackson이 제안한 작은 범위 가설 에 따르면 , 우리는 andAll함수를 3 또는 4와 같은 작은 길이까지만 테스트하면 됩니다. 최대 16 개의 테스트가 더 필요합니다. 따라서 총 21 개의 테스트가 수행됩니다. 물론 125 대신에 몇 가지 테스트 를 수행하고 싶을 것입니다 .allPrime 있지만, 우리가 염려했던 입력 시나리오의 125 가지 조합을 모두 철저히 다루어야 할 의무는 없습니다. 몇 가지 행복한 길.

확실히 좋은 예이지만 분명한 시연이 필요했습니다. 그리고 패턴은 실제 소프트웨어로 확장됩니다. 개인 기능은 일반적으로 가장 낮은 수준의 빌딩 블록이므로 더 높은 수준의 논리를 생성하기 위해 종종 결합됩니다. 더 높은 수준에서, 우리는 다양한 조합으로 인해 더 낮은 수준의 물건을 더 많이 반복합니다.


먼저, 표시된 것과 같은 순수한 기능을 사용하여 이와 같은 조합을 테스트 할 필요가 없습니다. 호출 isPrime은 완전히 독립적이므로 모든 조합을 맹목적으로 테스트하는 것은 목적이 없습니다. 둘째, isPrimeprivate 이라는 순수한 기능을 표시하면 많은 디자인 규칙을 위반하여 어디서부터 시작 해야할지 모릅니다. isPrime공개 기능이어야합니다. 즉,이 극도로 나쁜 예에 관계없이 당신이 말하는 것을 얻습니다. 그러나 실제 소프트웨어 시스템에서는 이것이 거의 좋은 아이디어가 아닌 경우 조합 테스트를 수행 하려는 전제를 기반으로합니다 .
Matt Messersmith

매트 예 예가 이상적이지 않습니다. 그러나 원칙은 분명해야합니다.
Colm Bhandal

전제는 조합 테스트를 수행하려는 것이 아닙니다. 퍼블릭 퍼즐 조각 만 테스트하도록 제한했다면 당신이해야 할 일입니다. 적절한 캡슐화 원칙을 준수하기 위해 순수한 기능을 비공개로 설정하려는 경우가 있습니다. 그리고이 순수한 개인 기능은 공공 기능에 의해 사용될 수 있습니다. 다른 순수한 개인 기능과 결합 된 방식으로. 이 경우 개인 테스트를하지 말아야한다는 교리에 따라 개인 구성 요소를 모듈 방식으로 테스트하는 대신 공용 기능에 대한 조합 테스트를 수행해야합니다.
Colm Bhandal

0

또한 메소드를 패키지 전용 (기본값)으로 만들 수 있으며 개인용이 아닌 한 단위 테스트를 수행 할 수 있어야합니다.

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