단위 테스트를 위해 개인용 메소드를 공개하는 것은 좋은 생각입니까?


301

중재자 참고 : 여기에 이미 39 개의 답변이 게시되어 있습니다 (일부는 삭제되었습니다). 답변 을 게시하기 전에 토론에 의미있는 것을 추가 할 수 있는지 여부를 고려 하십시오 . 다른 사람이 이미 말한 것을 반복했을 가능성이 높습니다.


가끔 단위 테스트를 작성하기 위해 클래스 공용에서 개인용 메소드를 작성해야하는 경우가 있습니다.

일반적으로 이것은 메서드가 클래스의 다른 메서드간에 공유되는 논리를 포함하고 자체적으로 논리를 테스트하기가 더 어렵 기 때문에 또는 스레드 문제에 대해 걱정할 필요없이 동기식 스레드에서 사용되는 논리를 테스트하고 싶을 수도 있습니다. .

다른 사람들이 스스로하는 일을합니까? 왜냐하면 저는 그 일을 정말 좋아하지 않기 때문입니다 ?? 개인적으로 보너스는 클래스 외부에서 서비스를 제공하지 않는 메소드를 공개하는 문제보다 중요하다고 생각합니다 ...

최신 정보

모두에게 답변을 주셔서 감사합니다, 사람들의 관심을 끌고있는 것 같습니다. 클래스가 사용될 유일한 방법이기 때문에 퍼블릭 API를 통해 테스트를 수행해야한다는 일반적인 합의가 있다고 생각합니다. 위에서 언급 한 두 가지 사례는 흔하지 않은 사례였으며 그로 인해 얻을 수있는 이점이 가치 있다고 생각했습니다.

그러나 모든 사람들이 결코 그런 일이 일어나지 않아야한다는 점을 알 수 있습니다. 그리고 조금 더 생각할 때 테스트를 수용하기 위해 코드를 변경하는 것은 나쁜 생각이라고 생각합니다. 결국 테스트가 일종의 지원 도구라고 생각하고 시스템을 '지원 도구를 지원하도록'변경하는 것이 뻔하다 고 생각합니다. 나쁜 연습.


2
"원하는 경우 시스템을 '지원 도구를 지원하도록'변경하는 것은 끔찍한 나쁜 관행입니다." 예, 일종의. 그러나 OTOH가 시스템이 기존 도구로 제대로 작동하지 않으면 시스템 문제가있을 수 있습니다.
Thilo


1
답은 아니지만 반영하여 언제든지 액세스 할 수 있습니다.
Zano

33
아니요, 노출해서는 안됩니다. 대신, 퍼블릭 API를 통해 클래스가 정상적으로 동작하는지 테스트해야합니다 . 그리고 내부를 노출시키는 것이 실제로 유일한 옵션이라면 (내가 의심하는 경우) 최소한 접근 자 패키지를 보호하여 테스트 해야하는 것과 동일한 패키지의 클래스 만 액세스 할 수 있도록해야합니다.
JB 니 제트

4
그렇습니다. 유닛 테스트에서 접근 할 수 있도록 메소드 패키지 전용 (기본) 가시성을 제공하는 것은 완벽하게 허용됩니다. 구아바와 같은 라이브러리 @VisibileForTesting는 그러한 방법을 만들기 위해 주석을 제공하기도합니다 . 방법이 아닌 이유 private가 제대로 문서화 되도록하는 것이 좋습니다 .
거미 보리스

답변:


186

참고 :
이 답변은 원래 질문에 대해 게시되었습니다. 단위 테스트만으로도 getter를 통해 프라이빗 인스턴스 변수를 노출해야하는 좋은 이유가 있습니까? 이것은 이것과 합쳐져서, 여기에 제시된 유스 케이스에 특유한 것이 될 수 있습니다.

일반적인 설명으로, 나는 보통 테스트하기 쉽도록 "프로덕션"코드를 리팩토링하는 일을합니다. 그러나 나는 이것이 좋은 전화라고 생각하지 않습니다. 좋은 단위 테스트는 (보통) 클래스의 구현 세부 사항, 가시적 인 동작에만 신경 쓰지 않아야합니다. 대신 시험에 내부 스택을 노출, 당신은 클래스를 사용하면 호출 한 후 그것을 기대하는 순서로 페이지를 반환하는지 테스트 할 수 first()또는 last().

예를 들어 다음 의사 코드를 고려하십시오.

public class NavigationTest {
    private Navigation nav;

    @Before
    public void setUp() {
        // Set up nav so the order is page1->page2->page3 and
        // we've moved back to page2
        nav = ...;
    }

    @Test
    public void testFirst() {
        nav.first();

        assertEquals("page1", nav.getPage());

        nav.next();
        assertEquals("page2", nav.getPage());

        nav.next();
        assertEquals("page3", nav.getPage());
    }

    @Test
    public void testLast() {
        nav.last();

        assertEquals("page3", nav.getPage());

        nav.previous();
        assertEquals("page2", nav.getPage());

        nav.previous();
        assertEquals("page1", nav.getPage());
    }
}

8
나는 당신의 대답에 전적으로 동의합니다. 많은 개발자들이 기본 액세스 수정자를 사용하고 싶은데, 훌륭한 오픈 소스 프레임 워크가이 전략을 사용하므로 정확해야합니다. 그렇다고해서 꼭해야한다는 의미는 아닙니다. +1
CKing

6
다른 사람들이 유용한 통찰력을 제공했지만 이것이이 문제에 가장 적합한 해결책이라고 말해야합니다. 의도 된 동작이 내부적으로 어떻게 달성되는지 테스트를 완전히 무시합니다. +10!
Gitahi Ng'ang'a

그런데 @Mureinik이 제공 한 예에서 테스트 방법이 테스트중인 실제 방법보다 더 많이 포함된다는 것이 괜찮습니까? 예를 들어, testFirst ()는 next ()를 호출하는데, 여기서 실제로 테스트중인 메소드는 아니지만 first ()입니다. 네 가지 탐색 방법을 모두 포함하는 하나의 테스트 방법 만있는 것이 더 깨끗하고 명확하지 않습니까?
Gitahi Ng'ang'a

1
이것이 이상적이기는하지만, 구성 요소가 성공했을뿐만 아니라 올바른 방법으로 구성 요소를 점검해야 할 때가 있습니다. 블랙 박스 테스트가 더 어려울 수 있습니다. 때로는 컴포넌트를 화이트 박스 테스트해야합니다.
피터로 레이

1
단위 테스트를 위해 게터를 만드는가? 이는 인터페이스가 데이터가 아닌 동작을 노출해야한다는 점에서 OOP / Object Thinking 의 결정에 반 합니다.
IntelliData

151

개인적으로, 나는 대중 API를 사용하지 않고 단위 테스트를 거라고 나는 확실히 개인 방법 공개는하지 않습니다 않겠다고 단지 시험에 쉽게 그것을 만들 수 있습니다.

개인 메소드를 분리하여 테스트하고 싶다면 Java에서 Easymock / Powermock 을 사용하여이를 수행 할 수 있습니다.

당신은 그것에 대해 실용적이어야하고 또한 사물을 테스트하기 어려운 이유를 알고 있어야합니다.

' 테스트 듣기 '-테스트하기 어려운 경우 디자인에 대해 이야기하고 있습니까? 이 방법에 대한 테스트가 사소하고 공개 API를 통한 테스트로 쉽게 다루어지는 위치를 리팩터링 할 수 있습니까?

여기에 무엇 마이클 깃털 것은 '에 말을 가지고있어 효과적으로 레거시 코드로 작업 "

"많은 사람들 이이 문제를 해결하는 방법을 알아 내려고 많은 시간을 소비합니다 ... 실제 답변은 개인 방법을 테스트 해야하는 충동이 있다면 방법이 비공개가되어서는 안된다는 것입니다. "당신을 귀찮게 할 것입니다. 그것은 별도의 책임의 일부이기 때문입니다. 다른 반에 있어야합니다." [ M. Feathers의 레거시 코드 (2005)로 효과적으로 작업 ]


5
Easymock / Powermock에 +1하고 절대 개인 메소드를 공개하지 마십시오
Leonard Brünings

51
+1 "테스트 듣기". 전용 메소드를 테스트해야한다고 생각되면 해당 메소드의 논리가 너무 복잡하여 별도의 헬퍼 클래스에 있어야합니다. 그런 다음 도우미 클래스를 테스트 할 수 있습니다.
Phil

2
'테스트 청취'가 다운 된 것으로 보입니다. 캐시 된 링크는 다음과 같습니다. webcache.googleusercontent.com/…
Jess Telford

1
@Phil (특히 도서 참조)에 동의하지만 종종 레거시 코드가있는 닭 / 계란 상황에 빠지게됩니다. 나는이 방법이 다른 클래스에 속한다는 것을 알고 있지만 처음에는 테스트없이 100 % 편안한 리팩토링이 아닙니다.
Kevin

나는 의사를 동의한다. 개인 메소드를 테스트 해야하는 경우 다른 클래스에 있어야합니다. 개인 메소드가있는 다른 클래스 / 도우미 클래스는 공개 / 보호 될 가능성이 있습니다.
rupweb

67

다른 사람들이 말했듯이, 개인 메소드를 단위 테스트하는 것이 다소 의심됩니다. 개별 구현 세부 정보가 아닌 공개 인터페이스를 단위 테스트합니다.

즉, C #에서 개인 단위로 테스트 할 때 사용하는 기술은 액세스 가능성 보호를 개인에서 내부로 다운 그레이드 한 다음 InternalsVisibleTo 사용하여 친구 테스트로 단위 테스트 어셈블리를 표시하는 것 입니다. 그러면 단위 테스트 어셈블리가 내부를 공개로 취급 할 수 있지만 실수로 공개 표면 영역에 추가하는 것에 대해 걱정할 필요는 없습니다.


이 기술도 사용하지만 일반적으로 레거시 코드의 최후의 수단으로 사용됩니다. 개인 코드를 테스트해야하는 경우 누락 된 클래스가 있거나 테스트중인 클래스가 너무 많은 것입니다. 해당 코드를 새로운 클래스로 추출하여 독립적으로 테스트 할 수 있습니다. 이와 같은 트릭은 거의 사용하지 않아야합니다.
Finglas

그러나 클래스를 추출하고 테스트하는 것만으로 테스트에서 원래 클래스가 실제로 추출 된 코드를 사용 하고 있다는 지식을 잃게됩니다 . 테스트에서이 틈을 메우는 방법은 무엇입니까? 그리고 답이 추출 된 논리가 사용되는 방식으로 원래 클래스의 공용 인터페이스를 테스트하는 것이라면 왜 처음에 그것을 추출하는 것이 귀찮습니까? (나는 여기서 대립을 말하는 것은 아니다. btw-나는 사람들이 어떻게 그러한 균형을 맞추는 지 항상 궁금해했다.)
Mal Ross

@mal 공동 작업 테스트를했습니다. 즉, 새 클래스를 확인하기위한 모의가 올바르게 호출되었습니다. 아래 답변을 참조하십시오.
Finglas

테스트하려는 내부 로직으로 클래스에서 상속되는 테스트 클래스를 작성하고 내부 로직 테스트에 사용할 공용 메소드를 제공 할 수 있습니까?
sholsinger

1
@sholsinger : C #에서 공개 클래스는 내부 클래스에서 상속 할 수 없습니다.
Eric Lippert

45

많은 답변은 공개 인터페이스 테스트 만 제안하지만 IMHO 이것은 비현실적입니다. 메서드가 5 단계를 수행하는 작업을 수행하는 경우 5 단계를 모두 테스트하지 않고 별도로 테스트해야합니다. 이 모든 다섯 가지 방법, 시험이 필요합니다 (테스트 이외의) 다른 수 있습니다을 private.

"비공개"메소드를 테스트하는 일반적인 방법은 모든 클래스에 고유 한 인터페이스를 부여하고 "비공개"메소드 public를 작성하지만 인터페이스에는 포함시키지 않는 것입니다. 이런 식으로, 그들은 여전히 ​​테스트 할 수 있지만 인터페이스를 팽창시키지 않습니다.

예, 이렇게하면 파일과 클래스가 팽창합니다.

예, 이렇게하면 publicprivate지정자가 중복됩니다.

예, 이것은 엉덩이에 통증이 있습니다.

불행히도 이것은 코드를 테스트 할 수있게 만드는 많은 희생 중 하나입니다 . 아마도 미래 언어 (또는 미래 버전의 C # / Java)에는 클래스 및 모듈 테스트 가능성을보다 편리하게 만드는 기능이있을 것입니다. 그러나 그 동안 우리는이 후프를 뛰어 넘어야합니다.


각 단계가 자신의 클래스 여야한다고 주장하는 사람들도 있지만 동의하지 않습니다. 모두 공유 상태라면 5 가지 방법으로 5 가지 클래스를 만들 이유가 없습니다. 더 나쁜 것은 이로 인해 파일 및 클래스 팽창이 발생한다는 것입니다. 또한 모듈의 공개 API를 감염시킵니다. 다른 모든 클래스 public에서 테스트하려는 경우 해당 클래스가 있어야합니다 (또는 테스트 코드를 동일한 모듈에 포함 시키므로 테스트 코드를 제품과 함께 제공함). .


2
상반기 좋은 답변
cmcginty

7
+1 : 가장 좋은 대답입니다. 자동차 제조업체가 같은 이유로 자동차의 일부를 테스트하지 않으면 아무도 그것을 사지 않을 것입니다. 코드를 공개로 변경하더라도 중요한 코드 부분을 테스트해야합니다.
태 성 신

1
아주 좋은 대답입니다. 당신은 내 따뜻한 투표를했습니다. 나는 대답을 추가했다. 관심이있을 수 있습니다.
davidxxx

29

단위 테스트는 클래스의 다른 부분에서 클래스를 사용하는 유일한 방법 인 공개 계약을 테스트해야합니다. 비공개 메소드는 구현 세부 사항이므로 공개 API가 올바르게 작동하는 한 테스트 사례를 변경하지 않고 구현이 중요하지 않고 변경 될 수있는 경우 테스트하지 않아야합니다.



Afair, 테스트 케이스에서 개인을 사용하기로 결정한 드문 상황은 모든 JNI 핸들러를 닫은 경우 객체 정확성을 확인하는 경우였습니다. 분명히 공개 API로 노출되지는 않지만 발생하지 않으면 메모리 누수가 발생합니다. 그러나 재미있는 것은 나중에 공개 API의 일부로 메모리 누수 탐지기를 도입 한 후에도 제거했습니다.
kan

11
이것은 결함이있는 논리입니다. 단위 테스트의 요점은 나중에 오류를 빨리 발견하는 것입니다. 비공개 메소드를 테스트하지 않으면 테스트에서 나중에 발견되지 않을 수있는 공백이 생깁니다. 개인 메소드가 복잡하고 공용 인터페이스로 쉽게 실행되지 않는 경우 단위 테스트를 수행해야합니다.
cmcginty

6
@Casey : 개인 인터페이스가 공용 인터페이스에 의해 실행되지 않으면 왜 존재합니까?
Thilo

2
@DaSilva_Ireland : 향후 테스트 사례를 유지하기가 어려울 것입니다. 유일한 실제 클래스 유스 케이스는 공용 메소드를 통한 것입니다. 즉, 다른 모든 코드는 공용 API를 통해서만 클래스를 사용할 수 있습니다. 따라서 구현 및 개인 메소드 테스트 사례 실패를 변경하려는 경우, 개인 테스트 만 수행하고 공개 테스트를 완전히 수행하지 않은 경우, 잘못된 것을 수행한다는 의미는 아니며 일부 잠재적 인 버그는 다루지 않습니다. . 이상적으로 테스트 사례는 가능한 모든 사례와 실제 클래스 사용 사례 만 포함해야합니다.
kan

22

IMO, 당신은 당신의 클래스가 어떻게 구현되었는지에 대해 깊이 가정하지 않는 테스트를 작성해야합니다. 나중에 다른 내부 모델을 사용하여 리팩토링하고 싶을 수도 있지만 이전 구현에서 제공하는 것과 동일한 보장을 계속합니다.

이를 염두에 두면서 현재 클래스의 내부 구현에 관계없이 계약이 여전히 유지되고 있는지 테스트하는 데 집중하는 것이 좋습니다. 퍼블릭 API의 속성 기반 테스트.


6
코드 리팩토링 후에 다시 작성할 필요가없는 단위 테스트가 더 좋다는 데 동의합니다. 단위 테스트를 다시 작성하는 데 소요되는 시간 때문 만이 아닙니다. 훨씬 더 중요한 이유는 단위 테스트의 가장 중요한 목적이 리팩토링 후에도 코드가 여전히 올바르게 작동한다는 것입니다. 리팩토링 후에 모든 단위 테스트를 다시 작성해야하는 경우 단위 테스트의 이점을 잃게됩니다.
kasperd

20

비공개 패키지로 만드는 것은 어떻습니까? 그런 다음 테스트 코드에서 코드 및 패키지의 다른 클래스를 볼 수 있지만 여전히 사용자에게 숨겨져 있습니다.

그러나 실제로는 개인 메소드를 테스트해서는 안됩니다. 이는 구현 세부 사항이며 계약의 일부가 아닙니다. 그들이하는 모든 것은 공개 메소드를 호출하여 다루어야합니다 (공개 메소드로 실행되지 않는 코드가있는 경우 계속 진행해야 함). 개인 코드가 너무 복잡하면 클래스가 너무 많은 일을하고 리팩토링을 원할 것입니다.

메소드를 공개하는 것은 큰 노력입니다. 일단 그렇게하면 사람들은 그것을 사용할 수 있으며 더 이상 변경할 수 없습니다.


당신의 개인을 테스트 해야하는 경우 +1 당신은 아마도 수업을 잃었을 것입니다
jk.

수업 누락으로 +1 다른 사람들이 "내부로 표시"악 대차에 바로 뛰어 들지 않는 것을 보게되어 기쁘다.
Finglas

소년 나는 패키지 개인 답변을 찾기 위해 먼 길을 가야했습니다.
Bill K

17

업데이트 : 나는이 질문에 대한 더 광범위하고 완전한 답변을 다른 여러 곳에서 추가했습니다. 이것은 내 블로그에서 찾을 수 있습니다 .

테스트하기 위해 공개해야 할 것이 있다면, 이는 테스트중인 시스템이 단일 책임 원칙을 따르지 않는다는 것을 암시합니다 . 따라서 소개해야 할 누락 된 클래스가 있습니다. 코드를 새 클래스로 추출한 후 공개하십시오. 이제 쉽게 테스트 할 수 있으며 SRP를 따르고 있습니다. 다른 클래스는 단순히 컴포지션을 통해이 새 클래스를 호출해야합니다.

어셈블러를 테스트하기 위해 코드를 보이는 것으로 표시하는 등의 언어 트릭을 공개 / 사용하는 방법은 항상 최후의 수단이되어야합니다.

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

public class SystemUnderTest
{
   public void DoStuff()
   {
      // Blah
      // Call Validate()
   }

   private void Validate()
   {
      // Several lines of complex code...
   }
}

유효성 검증기 오브젝트를 도입하여이를 리 팩터하십시오.

public class SystemUnderTest
{
    public void DoStuff()
    {
       // Blah
       validator.Invoke(..)
    }
}

이제 유효성 검사기가 올바르게 호출되었는지 테스트하기 만하면됩니다. 실제 유효성 검사 프로세스 (이전의 개인 논리)는 순수 격리로 테스트 할 수 있습니다. 이 유효성 검사를 통과하기 위해 복잡한 테스트 설정이 필요하지 않습니다.


1
개인적으로 SRP를 너무 많이 취할 수 있다고 생각합니다. 예를 들어 응용 프로그램에서 계정을 업데이트 / 생성 / 삭제하는 계정 서비스를 작성하는 경우 각 작업마다 별도의 클래스를 만들어야한다고 말하고 있습니까? 예를 들어 유효성 검사는 어딘가에 사용되지 않을 때 다른 클래스로 유효성 검사 논리를 추출 할 가치가 있습니까? 코드를 읽기 어렵고 간결하게 만들고 응용 프로그램을 불필요한 클래스로 압축하는 imo!
jcvandan

1
그것은 모두 시나리오에 달려 있습니다. "한 일을하는 것"에 대한 한 사람의 정의는 다른 사람과 다를 수 있습니다. 이 경우 테스트하려는 개인 코드는 복잡하고 설정하기가 쉽지 않습니다. 테스트하려는 사실은 너무 많은 일을하고 있음을 증명합니다. 따라서 새로운 물체를 갖는 것이 이상적입니다.
Finglas

1
코드를 읽기 어렵게 만드는 것에 대해서는 동의하지 않습니다. 유효성 검사 클래스가 있으면 다른 개체에 유효성 검사가 포함되어있는 것보다 읽기 쉽습니다. 여전히 같은 양의 코드가 있으며 하나의 큰 클래스에 중첩되어 있지 않습니다. 오히려 하나의 큰 클래스보다 한 가지 일을 잘하는 여러 클래스가 있습니다.
Finglas

4
@dormisher는 계정 서비스와 관련하여 SRP를 "단일 변경해야 할 이유"라고 생각합니다. CRUD 작업이 자연스럽게 함께 변경되면 SRP에 적합합니다. 일반적으로 데이터베이스 스키마가 변경되어 삽입 및 업데이트에 자연적으로 영향을 미치므로 항상 변경하지는 않으므로 변경하십시오.
Anthony Pegram

@finglas 당신은 이것에 대해 어떻게 생각하는지 : stackoverflow.com/questions/29354729/...을 . 개인 메서드를 테스트하고 싶지 않으므로 클래스로 분리해서는 안되지만 다른 두 가지 공용 메서드에서 사용되므로 동일한 논리를 두 번 테스트하게됩니다.
Rodrigo Ruiz

13

좋은 답변입니다. 내가 언급하지 않은 한 가지는 테스트 중심 개발 (TDD)을 사용하여 리팩토링 단계에서 개인 메소드가 작성되므로 ( 리팩토링 패턴의 예에 대한 추출 메소드 참조 ) 이미 필요한 테스트 범위를 가지고 있어야합니다. . 올바르게 수행되면 (물론 정확성에 관해서는 다양한 의견이 나올 것입니다), 테스트 할 수 있도록 개인용 메소드를 공개해야하는 것에 대해 걱정할 필요가 없습니다.


11

스택 관리 알고리즘을 유틸리티 클래스로 분리하지 않는 이유는 무엇입니까? 유틸리티 클래스는 스택을 관리하고 공개 접근자를 제공 할 수 있습니다. 단위 테스트는 구현 세부 사항에 중점을 둘 수 있습니다. 알고리즘 적으로 까다로운 클래스에 대한 심층 테스트는 엣지 케이스를 축소하고 적용 범위를 보장하는 데 매우 유용합니다.

그런 다음 현재 클래스는 구현 세부 정보를 노출시키지 않고 유틸리티 클래스에 깔끔하게 위임 할 수 있습니다. 테스트는 다른 사람들이 권장 한 것처럼 페이지 매김 요구 사항과 관련이 있습니다.


10

Java에는 패키지를 비공개 로 만드는 옵션도 있습니다 (예 : 가시성 수정 자 제외). 단위 테스트가 테스트중인 클래스와 동일한 패키지에있는 경우 이러한 메소드를 볼 수 있어야하며 메소드를 완전히 공개하는 것보다 조금 더 안전합니다.


10

전용 메소드는 일반적으로 "도우미"메소드로 사용됩니다. 따라서 기본 값만 반환하고 특정 개체 인스턴스에서는 작동하지 않습니다.

테스트하려는 경우 몇 가지 옵션이 있습니다.

  • 반사 사용
  • 메소드에 패키지 액세스 권한 부여

또는 새 클래스에 적합한 후보 인 경우 도우미 메소드를 공용 메소드로 사용하여 새 클래스를 작성할 수 있습니다.

여기 에 아주 좋은 기사가 있습니다 .


1
공정한 의견. 옵션 일 수도 있고 아마도 나쁜 옵션 일 수도 있습니다.
adamjmarkham

@Finglas 리플렉션을 사용하여 개인 코드를 테스트하는 것이 왜 나쁜 생각입니까?
Anshul

@Anshul 개인 메소드는 구현 세부 사항이며 동작이 아닙니다. 다시 말해, 그들은 변한다. 이름이 바뀝니다. 매개 변수가 변경됩니다. 내용이 바뀝니다. 즉, 이러한 테스트는 항상 중단됩니다. 반면에 퍼블릭 메소드는 API 측면에서 훨씬 안정적이므로 탄력적입니다. 퍼블릭 메소드에 대한 테스트를 제공하여 구현 세부 정보가 아닌 동작을 확인하면 좋을 것입니다.
Finglas

1
@Finglas 왜 구현 세부 사항을 테스트하고 싶지 않습니까? 작동 해야하는 코드이며 더 자주 변경되면 더 자주 중단 될 것으로 예상되므로 공개 API보다 더 많이 테스트해야합니다. 코드베이스가 안정되면 언젠가 누군가가 와서 클래스의 내부 작동 방식을 깨뜨리는 개인 메소드를 변경한다고 가정 해 봅시다. 수업 내부를 테스트하는 테스트는 즉시 무언가를 깨뜨렸다 고 알려줍니다. 그렇습니다. 테스트 유지 관리가 어려워 지지만 전체 범위 테스트에서 기대할 수 있습니다.
Anshul

1
@Finglas 테스트 구현 세부 사항이 잘못되지 않았습니다. 그것이 당신의 입장이지만, 이것은 널리 논의되는 주제입니다. 내부를 테스트하면 전체 범위에 필요하지 않더라도 몇 가지 장점이 있습니다. 테스트에 혼란을 줄 수있는 퍼블릭 API를 거치지 않고 프라이빗 메서드를 직접 테스트하기 때문에 테스트에 더욱 집중합니다. 개인 구성원을 수동으로 무효화하고 일관성없는 내부 상태에 대한 응답으로 공개 API가 적절한 오류를 리턴하는지 확인하기 때문에 부정적인 테스트가 더 쉽습니다. 더 많은 예가 있습니다.
Anshul

9

C #을 사용하는 경우 내부 메소드를 만들 수 있습니다. 그렇게하면 공용 API를 오염시키지 않습니다.

그런 다음 dll에 속성을 추가하십시오.

[조립품 : InternalsVisibleTo ( "MyTestAssembly")]

이제 모든 메소드가 MyTestAssembly 프로젝트에 표시됩니다. 아마도 완벽하지는 않지만 테스트하기 위해 개인 메소드를 공개하는 것이 좋습니다.


7

필요한 경우 리플렉션을 사용하여 개인 변수에 액세스하십시오.

그러나 실제로 클래스의 내부 상태에 신경 쓰지 않고 공개 메소드가 예상 할 수있는 상황에서 예상 한 것을 반환하는지 테스트하려고합니다.


void 메소드로 무엇을 하시겠습니까?
IntelliData

@IntelliData 필요한 경우 리플렉션을 사용하여 전용 변수에 액세스합니다.
Daniel Alexiuc

6

나는 그것이 당신이 이익을 얻었는지 잠재적으로 문제가 있는지 확실하지 않은 것이 나쁜 생각이라고 말하고 싶습니다. 개인 계약을 테스트하기 위해 통화 계약을 변경하는 경우 클래스 사용법을 테스트하는 것이 아니라 의도하지 않은 인공 시나리오를 만드는 것입니다.

또한, 방법을 공개로 선언함으로써 6 개월 동안 (메소드를 공개 한 유일한 이유는 테스트 용이라는 것을 잊은 후) 당신이 (또는 프로젝트를 넘겨받은 경우) 누군가 완전히 다른 원을 얻는다는 말 이를 사용하지 않으면 의도하지 않은 결과 및 / 또는 유지 보수의 악몽이 발생할 수 있습니다.


1
클래스가 서비스 계약의 구현이고 그것이 단지 인터페이스를 통해서만 사용된다면
어떨까요?

내부 API를 분할하기 위해 클래스를 리팩터링하면 테스트를 변경해야하기 때문에 공개 API (인터페이스)를 사용하여 클래스를 테스트하고 싶습니다. 새로운 테스트가 이전 테스트와 동일하게 작동하도록 노력합니다.
beny23

또한 테스트 적용 범위에 전용 메서드 내부의 모든 엣지 사례를 포함시키는 유일한 방법은 직접 호출하는 것이라면 클래스 복잡성으로 인해 단순화하기 위해 리팩토링이 필요하다는 것을 나타낼 수 있습니다.
beny23

@dormisher-클래스가 인터페이스를 통해서만 사용되는 경우 공용 인터페이스 메소드가 올바르게 작동하는지 테스트하면됩니다. 공개 방법이 의도 한대로 작동하는 경우 왜 개인 방법에 관심이 있습니까?
Andrzej Doyle

@Andrzej-실제로는 안된다고 생각하지만 모델 상태를 확인하는 개인 메소드와 같이 유효한 것으로 생각되는 상황이 있다고 가정합니다. 이와 같은 메소드를 테스트 할 가치가 있습니다. 그러나 이와 같은 메소드는 다음을 통해 테스트 할 수 있어야합니다 어쨌든 공개 인터페이스
jcvandan

6

단위 테스트 측면에서 더 많은 메소드를 추가해서는 안됩니다. 나는 당신이 당신의 first()메소드 에 대해 테스트 케이스를 만드는 것이 더 좋을 것이라고 믿는다 . 각 테스트 전에 호출 될 것이다. 당신은 여러 번 호출 할 수 있습니다 - next(), previous()그리고 last()성과가 당신의 기대를 일치하는지 확인합니다. 테스트 목적으로 만 클래스에 더 많은 메소드를 추가하지 않으면 "블랙 박스"테스트 원칙을 고수 할 것입니다.



5

업데이트에서 공개 API를 사용하여 테스트하는 것이 좋다고 말합니다. 실제로 두 개의 학교가 있습니다.

  1. 블랙 박스 테스트

    블랙 박스 스쿨은 그 클래스가 아무도 그 구현을 볼 수없는 블랙 박스로 간주되어야한다고 말합니다. 이것을 테스트하는 유일한 방법은 공용 API를 사용하는 것입니다. 클래스의 사용자가 사용하는 것처럼.

  2. 화이트 박스 테스트.

    화이트 박스 스쿨은 자연스럽게 클래스 구현에 대한 지식을 사용하고 클래스가 제대로 작동하는지 테스트합니다.

나는 토론에 참여할 수 없다. 클래스 (또는 라이브러리 또는 기타)를 테스트하는 두 가지 방법이 있다는 것을 아는 것이 흥미로울 것이라고 생각했습니다.


4

실제로이 작업을 수행해야하는 상황이 있습니다 (예 : 복잡한 알고리즘을 구현할 때). 그냥 패키지 전용으로하면 충분합니다. 그러나 대부분의 경우 논리를 다른 클래스로 분해해야하는 너무 복잡한 클래스가있을 수 있습니다.


4

독립적으로 테스트하려는 개인 메서드는 클래스에 다른 "개념"이 묻혀 있음을 나타냅니다. 해당 "개념"을 자체 클래스로 추출하여 별도의 "단위"로 테스트하십시오.

이 비디오 를 보고 주제에 대해 정말 흥미로운 내용을 살펴보십시오 .


4

테스트에서 코드를 지시하지 마십시오. 나는 TDD 또는 다른 DD에 대해 말하고 있지 않습니다. 정확하게 당신의 요구입니다. 앱에 공개하기 위해 이러한 방법이 필요합니까? 그렇다면 테스트하십시오. 그렇지 않은 경우 테스트를 위해 공개하지 마십시오. 변수 및 기타와 동일합니다. 응용 프로그램의 요구에 따라 코드가 결정되고 테스트에서 요구가 충족되는지 테스트하십시오. (다시 테스트를 의미하는 것은 아니며 테스트 목표를 달성하기 위해 클래스 구조를 변경하는 것을 의미하지는 않습니다).

대신 "높은 테스트"를해야합니다. private 메소드를 호출하는 메소드를 테스트하십시오. 그러나 테스트는 "구현 결정"이 아니라 응용 프로그램 요구 사항을 테스트해야합니다.

예를 들어 (여기의 의사 코드);

   public int books(int a) {
     return add(a, 2);
   }
   private int add(int a, int b) {
     return a+b;
   } 

"add"를 테스트 할 이유가 없습니다. 대신 "books"를 테스트 할 수 있습니다.

테스트가 코드 디자인 결정을 내릴 수 있도록하지 마십시오. 결과를 얻는 방법이 아니라 예상 결과를 얻는 지 테스트하십시오.


1
"테스트에서 코드 디자인 결정을 내릴 수있게하지 마십시오."-동의하지 않습니다. 테스트하기 어려운 것은 코드 냄새입니다. 테스트 할 수 없다면 고장이 아닌지 어떻게 알 수 있습니까?
Alfred Armstrong

명확히하기 위해 예를 들어 라이브러리의 외부 API 디자인을 변경해서는 안된다는 데 동의합니다. 그러나 테스트하기 쉽도록 리팩토링해야 할 수도 있습니다.
Alfred Armstrong

테스트하기 위해 리팩토링해야 할 경우 걱정해야 할 코드 냄새가 아니라는 것을 알았습니다. 다른 문제가 있습니다. 물론 그것은 개인적인 경험이며, 우리는 아마도 견고한 데이터로 양방향으로 논쟁을 할 수 있습니다. 처음에는 "나쁜 디자인"이 아닌 "테스트하기 어려운"적이 없었습니다.
coteyr

이 솔루션은 값을 반환하는 과 같은 방법 에 유용합니다 . assert 에서 반환 유형을 확인할 수 있습니다 . 그러나 void 메소드를 확인하는 방법은 무엇입니까?
IntelliData

void 메소드는 무언가를하거나 존재하지 않아도됩니다. 그들이하는 일을 확인하십시오
coteyr

2

나는 단위 테스트의 보너스가 일부 회원의 가시성을 높이는 문제보다 중요하다는 데 동의하는 경향이 있습니다. 약간의 개선은 그것을 보호하고 가상으로 만든 다음 테스트 클래스에서 재정 의하여 공개하는 것입니다.

또는 기능을 별도로 테스트하려는 경우 디자인에서 누락 된 객체를 제안하지 않습니까? 어쩌면 별도의 테스트 가능한 클래스에 넣을 수도 있습니다 ... 기존 클래스는이 새로운 클래스의 인스턴스에 위임합니다.


2

나는 일반적으로 테스트 클래스를 테스트중인 클래스와 동일한 프로젝트 / 어셈블리에 유지합니다.
이렇게 internal하면 함수 / 클래스를 테스트 할 수 있도록 가시성이 필요 합니다.

이것은 테스트 과정을 걸러 내야하는 건물 프로세스를 다소 복잡하게 만듭니다. 모든 테스트 클래스의 이름을 지정 TestedClassTest하고 정규식을 사용하여 해당 클래스를 필터링하여 이를 달성합니다 .

이것은 물론 질문의 C # / .NET 부분에만 적용됩니다.


2

나는 종종 같은 방법이라는 것을 추가 할 것이다 validate, verify, check이 객체의 내부 상태를 테스트하기 위해 호출 할 수 있도록 클래스, 등.

때때로이 메소드는 ifdef 블록 (주로 C ++로 작성)으로 래핑되어 릴리스 용으로 컴파일되지 않습니다. 그러나 릴리스시 프로그램의 오브젝트 트리를 점검하는 검증 방법을 제공하는 것이 종종 유용합니다.


2

구아바에는 스코프 (패키지 또는 퍼블릭)가 확대 된 메소드를 표시하기위한 @VisibleForTesting 주석이 있습니다. 나는 같은 것에 @Private 주석을 사용한다.

공개 API를 테스트해야하지만, 일반적으로 공개되지 않은 것들을 얻는 것이 편리하고 합리적인 경우가 있습니다.

언제:

  • 클래스는 여러 클래스로 나눠서 읽기가 쉽지 않습니다.
  • 더 테스트 가능하게하기 위해
  • 내부에 테스트 액세스를 제공하면 트릭을 수행 할 수 있습니다.

종교가 공학을 능가하는 것 같습니다.


2

나는 보통 이들 메소드를 그대로두고 protected패키지 테스트를 같은 패키지 (그러나 다른 프로젝트 나 소스 폴더에) 안에 두어 클래스 로더가 동일한 네임 스페이스 내에 배치하기 때문에 모든 보호 된 메소드에 액세스 할 수 있습니다.


2

아니, 그 고양이를 스키닝하는 더 좋은 방법이 있기 때문입니다.

일부 단위 테스트 장치는 클래스 정의에서 매크로를 사용하여 테스트 모드에서 빌드 할 때 후크를 생성하도록 자동 확장됩니다. 매우 C 스타일이지만 작동합니다.

보다 쉬운 OO 관용구는 "비공개"가 아닌 "보호 된"테스트하려는 항목을 만드는 것입니다. 테스트 하네스는 테스트중인 클래스에서 상속 된 다음 모든 보호 된 멤버에 액세스 할 수 있습니다.

또는 "친구"옵션을 선택하십시오. 개인적으로 이것은 캡슐화 규칙을 위반하기 때문에 가장 좋아하는 C ++의 기능이지만 C ++이 일부 기능을 구현하는 방법에 필요합니다.

어쨌든 단위 테스트를 수행하는 경우 해당 멤버에 값을 주입해야 할 가능성이 높습니다. 화이트 박스 문자 메시지는 완벽하게 유효합니다. 그리고 정말 것이다 당신의 캡슐화를 휴식.


2

.Net에는 클래스의 PrivateObject전용 메소드에 액세스 할 수 있도록 특별히 고안된 특수 클래스 가 있습니다.

MSDN 또는 여기에서 스택 오버플로 에 대해 자세히 알아보십시오.

(지금까지 아무도 언급하지 않았는지 궁금합니다.)

이것으로 충분하지 않은 상황이 있습니다.이 경우에는 반사를 사용해야합니다.

그래도 개인 방법을 테스트하지 않는 일반적인 권장 사항을 준수하지만 평소와 같이 항상 예외가 있습니다.


1

다른 사람들의 의견에서 알 수 있듯이 단위 테스트는 공개 API에 중점을 두어야합니다. 그러나 장단점과 정당성을 제외하고 리플렉션을 사용하여 단위 테스트에서 개인 메서드를 호출 할 수 있습니다. 물론 JRE 보안이 허용하는지 확인해야합니다. private 메소드 호출은 Spring Framework가 ReflectionUtils 와 함께 사용하는 것입니다 ( makeAccessible(Method)메소드 참조 ).

다음은 전용 인스턴스 메소드가있는 작은 예제 클래스입니다.

public class A {
    private void doSomething() {
        System.out.println("Doing something private.");
    }
}

그리고 프라이빗 인스턴스 메소드를 실행하는 예제 클래스.

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class B {
    public static final void main(final String[] args) {
        try {
            Method doSomething = A.class.getDeclaredMethod("doSomething");
            A o = new A();
            //o.doSomething(); // Compile-time error!
            doSomething.setAccessible(true); // If this is not done, you get an IllegalAccessException!
            doSomething.invoke(o);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        }
    }
}

B를 실행하면 인쇄됩니다. Doing something private. 실제로 필요할 경우 리플렉션을 사용하여 프라이빗 인스턴스 메소드에 액세스 할 수 있습니다.


0

개인적으로 개인 메서드를 테스트 할 때도 동일한 문제가 있으며 일부 테스트 도구가 제한되어 있기 때문입니다. 제한된 도구로 인해 디자인이 아닌 도구를 변경해야하는 경우 디자인을 제한하는 것은 좋지 않습니다. C #을 요구하면 좋은 테스트 도구를 제안 할 수 없지만 Java의 경우 TestNG 및 PowerMock의 두 가지 강력한 도구가 있으며 .NET 플랫폼에 해당하는 테스트 도구를 찾을 수 있습니다

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