개인 메소드를 어떻게 단위 테스트합니까?


184

Java 프로젝트를 진행 중입니다. 단위 테스트를 처음 사용합니다. Java 클래스에서 개인 메소드를 단위 테스트하는 가장 좋은 방법은 무엇입니까?


1
이 질문은 StackOverflow에서 확인하십시오. 몇 가지 기술이 언급되고 논의됩니다. 개인 메소드를 단위 테스트하는 가장 좋은 방법은 무엇입니까?
Chiron

34
내 의견은 항상 사용할 수있는 것을 테스트해야하기 때문에 개인 메서드는 테스트가 필요 없다는 것입니다. 공개 방법. 퍼블릭 메소드를 깰 수 없다면 실제로 프라이빗 메소드가 무엇을 하는가가 중요합니까?
Rig

1
공개 및 개인 방법을 모두 테스트해야합니다. 따라서 테스트 드라이버는 일반적으로 테스트하는 클래스 내에 있어야합니다. 같은 .
overexchange

2
@Rig-+1-퍼블릭 메서드에서 프라이빗 메서드의 모든 필수 동작을 호출 할 수 있어야합니다. 그렇지 않으면 기능을 호출 할 수 없으므로 테스트 할 필요가 없습니다.
제임스 앤더슨

매니 폴드 프레임 워크 @Jailbreak에서 사용 하여 개인 메소드에 직접 액세스하십시오. 이런 식으로 테스트 코드는 형식 안전 하고 읽을 수 있습니다. 무엇보다도, 테스트를위한 디자인 타협, 과도한 노출 방법 및 필드가 없습니다.
스콧

답변:


239

일반적으로 개인 메소드를 직접 단위 테스트하지 않습니다. 그것들은 비공개이므로 구현 세부 사항을 고려하십시오. 아무도 그들 중 하나를 부르지 않고 특정 방식으로 작동 할 것으로 기대하지 않습니다.

대신 공용 인터페이스를 테스트해야합니다. 개인 메소드를 호출하는 메소드가 예상대로 작동하는 경우 확장하여 개인 메소드가 올바르게 작동한다고 가정합니다.


39
bazillion +1 그리고 개인 메소드가 호출되지 않으면 단위 테스트하지 마십시오. 삭제하십시오!
Steven A. Lowe

246
동의하지 않습니다. 때로는 프라이빗 메소드가 구현 세부 사항 일 뿐이지 만 테스트를 보증하고 제대로 작동하는지 확인하기에는 여전히 복잡합니다. 공용 인터페이스는이 특정 알고리즘을 직접 대상으로하는 테스트를 작성하기에는 너무 높은 추상화 수준을 제공 할 수 있습니다. 공유 데이터로 인해 별도의 클래스로 인수 분해하는 것이 항상 가능한 것은 아닙니다. 이 경우 개인 메서드를 테스트해도 괜찮습니다.
quant_dev

10
물론 삭제하십시오. 사용하지 않는 함수를 삭제하지 않는 유일한 이유는 나중에 필요할지도 모른다고 생각하는 경우에도 삭제하지만 커밋 로그에 함수를 기록하거나 적어도 주석 처리하여 주석을 달아야하기 때문입니다. 사람들은 그것이 무시할 수있는 추가 물건이라는 것을 알고 있습니다.
jhocking

38
@quant_dev : 때때로 클래스를 다른 여러 클래스 로 리팩토링해야합니다 . 공유 데이터를 별도의 클래스로 가져올 수도 있습니다. "컨텍스트"클래스를 가정 해 봅시다. 그러면 두 개의 새 클래스가 공유 데이터의 컨텍스트 클래스를 참조 할 수 있습니다. 이것은 불합리한 것처럼 보일 수 있지만 개인 방법이 개별 테스트가 필요할 정도로 복잡하면 객체 그래프가 좀 더 세분화되어야 함을 나타내는 코드 냄새입니다.
Phil

43
이 답변 은 질문에 대답 하지 않습니다 . 문제는 어떻게 개인 방법이 없습니다, 테스트해야 하는지 여부 가 테스트해야합니다. 개인 메소드를 단위 테스트해야하는지 여부는 흥미로운 질문이자 토론 할만한 가치가 있지만 여기서는 아닙니다 . 적절한 답변은 비공개 메소드를 테스트하는 것은 좋은 생각이 아닐 수 있다는 질문에 의견을 추가하고 문제에 대해 더 깊이 들어가는 별도의 질문에 대한 링크를 제공하는 것입니다.
TallGuy

118

일반적으로 나는 그것을 피할 것입니다. 개인 메소드가 너무 복잡하여 별도의 단위 테스트가 필요한 경우 자체 클래스가 필요하다는 의미입니다. 재사용 가능한 방식으로 작성하도록 권장 할 수 있습니다. 그런 다음 새 클래스를 테스트하고 이전 클래스에서 해당 클래스의 공용 인터페이스를 호출해야합니다.

반면에 구현 세부 사항을 별도의 클래스로 분류하면 복잡한 인터페이스가있는 클래스, 이전 클래스와 새 클래스 사이에 많은 데이터가 전달되는 클래스 또는 OOP 관점에서 잘 보일 수있는 디자인으로 이어지지 만 때로는 그렇지 않습니다. 문제 영역에서 오는 직관과 일치하십시오 (예 : 개인 메소드 테스트를 피하기 위해 가격 책정 모델을 두 조각으로 나누는 것은 매우 직관적이지 않으며 나중에 코드를 유지 관리 / 연장 할 때 문제가 발생할 수 있음). 항상 함께 바뀌는 "트윈 클래스"를 원하지 않습니다.

캡슐화와 테스트 가능성 사이의 선택에 직면했을 때, 나는 두 번째로 가고 싶습니다. 적절하게 테스트되지 않았기 때문에 올바르게 작동하지 않는 멋진 OOP 디자인보다 올바른 코드 (즉, 올바른 출력 생성)를 갖는 것이 더 중요합니다. Java에서는 메소드에 "기본"액세스 권한을 부여하고 단위 테스트를 동일한 패키지에 넣을 수 있습니다. 단위 테스트는 개발중인 패키지의 일부일 뿐이므로 테스트와 테스트중인 코드간에 종속성이있는 것이 좋습니다. 구현을 변경할 때 테스트를 변경해야 할 수도 있지만, 구현마다 변경 될 때마다 코드를 다시 테스트해야하며 테스트를 수정하여 수정해야하는 경우에는 그것.

일반적으로 클래스는 둘 이상의 인터페이스를 제공 할 수 있습니다. 사용자를위한 인터페이스와 관리자를위한 인터페이스가 있습니다. 두 번째 코드는 코드가 적절히 테스트되도록 더 많이 노출시킬 수 있습니다. 개인 메서드에 대한 단위 테스트 일 필요는 없습니다 (예 : 로깅). 로깅도 "캡슐화를 중단"하지만 여전히 유용하기 때문에 여전히 수행합니다.


9
+1 재미있는 포인트. 결국 그것은 좋은 OOP의 "규칙"을 지키지 않고 유지 보수성에 관한 것입니다.
Phil

9
+1 캡슐화에 대한 테스트 가능성에 전적으로 동의합니다.
Richard

31

프라이빗 메소드 테스트는 복잡성에 따라 다릅니다. 일부 한 줄의 개인 메서드는 테스트의 추가 노력을 실제로 보증하지는 않지만 (공개 메서드라고도 할 수 있음) 일부 개인 메서드는 공용 메서드와 마찬가지로 복잡하고 공용 인터페이스를 통해 테스트하기가 어려울 수 있습니다.

내가 선호하는 기술은 개인 메소드 패키지를 비공개로 만드는 것인데, 동일한 패키지에서 단위 테스트에 액세스 할 수는 있지만 여전히 다른 모든 코드에서 캡슐화됩니다. 이것은 복잡한 논리의 모든 부분을 다루기 위해 공개 메소드 테스트에 의존하지 않고 개인 메소드 논리를 직접 테스트하는 이점을 제공합니다.

이것이 Google Guava 라이브러리의 @VisibleForTesting 주석과 쌍을 이루는 경우,이 패키지 전용 메소드를 테스트 용으로 만 표시되도록 명시하고 있으므로 다른 클래스에서 호출해서는 안됩니다.

이 기술의 반대자들은 이것이 캡슐화를 깨뜨리고 개인 패키지를 열어서 동일한 패키지로 코딩 할 것이라고 주장합니다. 나는 이것이 캡슐화를 깨고 다른 클래스에 개인 코드를 공개한다는 것에 동의하지만, 복잡한 논리 테스트는 엄격한 캡슐화 보다 중요 하며 테스트를 위해 눈에 잘 띄는 패키지 전용 메소드를 사용하지 않는 것은 개발자의 책임이어야 함을 주장합니다 코드베이스 사용 및 변경

테스트 전 개인 방법 :

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

테스트 준비가 된 패키지 전용 메소드 :

@VisibleForTesting
int add(int a, int b){
    return a + b;
}

참고 : 테스트를 동일한 패키지에 넣는 것은 동일한 실제 폴더에 넣는 것과 다릅니다. 기본 코드와 테스트 코드를 별도의 실제 폴더 구조로 분리하는 것이 일반적으로 좋은 방법이지만이 방법은 클래스가 동일한 패키지에 정의되어있는 한 작동합니다.


14

외부 API를 사용할 수 없거나 원하지 않는 경우 순수한 표준 JDK API를 사용하여 리플렉션을 사용하여 개인 메소드에 액세스 할 수 있습니다. 여기에 예가 있습니다

MyObject obj = new MyObject();
Method privateMethod = MyObject.class.getDeclaredMethod("getFoo", null);
privateMethod.setAccessible(true);
String returnValue = (String) privateMethod.invoke(obj, null);
System.out.println("returnValue = " + returnValue);

Java 학습서 http://docs.oracle.com/javase/tutorial/reflect/ 또는 Java API http://docs.oracle.com/javase/7/docs/api/java/lang/reflect/package-summary를 확인 하십시오. 자세한 내용은 html 을 참조하십시오.

@kij가 자신의 답변을 인용 한 것처럼 리플렉션을 사용하는 간단한 솔루션이 실제로 개인 메서드를 테스트하는 것이 좋은 경우가 있습니다.


9

단위 테스트 사례는 코드 단위를 테스트하는 것을 의미합니다. 인터페이스를 테스트하는 경우 코드 단위를 테스트한다는 의미는 아니기 때문에 인터페이스를 테스트하는 것은 아닙니다. 일종의 블랙 박스 테스트가됩니다. 또한 인터페이스 수준에서 문제를 확인한 다음 작동하지 않는 부분을 디버깅하는 것보다 가장 작은 단위 수준에서 문제를 찾는 것이 좋습니다. 따라서 단위 테스트 케이스는 범위와 상관없이 테스트해야합니다. 다음은 개인용 메소드를 테스트하는 방법입니다.

java를 사용하는 경우 Deencapsulation.invoke를 제공하는 jmockit을 사용하여 테스트중인 클래스의 개인용 메소드를 호출 할 수 있습니다. 리플렉션을 사용하여 결국 호출하지만 멋진 래퍼를 제공합니다. ( https://code.google.com/p/jmockit/ )


이것은 질문에 어떻게 대답합니까?
gnat

@ gnat, 질문은 자바 클래스에서 개인 메소드를 단위 테스트하는 가장 좋은 방법은 무엇입니까? 그리고 첫 번째 요점은 그 질문에 대한 답입니다.
agrawalankur 2019

첫 번째 요점은 단지 도구를 광고하는 것이지만, 예를 들어 최고 답변에서 제안되고 설명 된대로
gnat

jmockit이 이전했으며 이전 공식 페이지는 "합성 소변 및 인공 오줌"에 대한 기사를 가리 킵니다. Wich는 여전히 mocking stuff와 관련이 있지만 여기서는 관련이 없습니다 :) 새로운 공식 페이지는 다음과 같습니다 : jmockit.github.io
Guillaume

8

우선, 다른 저자들이 제안한 바와 같이 : 개인 방법을 실제로 테스트해야한다면 두 번 생각하십시오. 그리고 그렇다면 ...

.NET에서는 "내부"방법으로 변환 할 수 있으며, 패키지 "할 InternalVisible을 당신의 단위 테스트 프로젝트에".

Java에서는 테스트 할 클래스에 테스트 자체를 작성할 수 있으며 테스트 메소드는 개인 메소드도 호출 할 수 있어야합니다. 실제로 Java 환경이 크지 않으므로 최상의 방법은 아닙니다.

감사.


7

나는 보통 그러한 방법들을 보호한다. 수업이 있다고 가정 해 봅시다.

src/main/java/you/Class.java

다음과 같이 테스트 클래스를 만들 수 있습니다.

src/test/java/you/TestClass.java

이제 보호 된 메소드에 액세스 할 수 있으며 단위 테스트를 수행 할 수 있지만 (JUnit 또는 TestNG는 중요하지 않음)이 메소드를 원하지 않는 호출자로부터 유지합니다.

이것은 maven 스타일의 소스 트리를 필요로합니다.


3

Java를 사용하여 개인 메소드를 테스트 해야하는 경우 fest assertand 및 / 또는을 사용할 수 있습니다 fest reflect. 반사를 사용합니다.

maven으로 라이브러리를 가져 오거나 (주어진 버전은 내가 생각하는 최신 버전이 아닙니다) 클래스 경로에서 직접 가져 오십시오.

<dependency>
     <groupId>org.easytesting</groupId>
     <artifactId>fest-assert</artifactId>
     <version>1.4</version>
    </dependency>

    <dependency>
     <groupId>org.easytesting</groupId>
     <artifactId>fest-reflect</artifactId>
    <version>1.2</version>
</dependency>

예를 들어, 'myPrivateMethod'라는 개인 메소드가있는 'MyClass'라는 클래스가있는 경우 String을 매개 변수로 사용하여 값을 'this is cool testing!'로 업데이트하면 다음 junit 테스트를 수행 할 수 있습니다.

import static org.fest.reflect.core.Reflection.method;
...

MyClass objectToTest;

@Before
public void setUp(){
   objectToTest = new MyClass();
}

@Test
public void testPrivateMethod(){
   // GIVEN
   String myOriginalString = "toto";
   // WHEN
   method("myPrivateMethod").withParameterTypes(String.class).in(objectToTest).invoke(myOriginalString);
   // THEN
   Assert.assertEquals("this is cool testing !", myOriginalString);
}

이 라이브러리를 사용하면 모든 빈 속성을 비공개로 설정하고 세터를 작성하지 않아도 Mockito 또는 다른 Mock Framework와 함께 사용할 수 있습니다. 현재 알아야 할 유일한 것은 (다음 버전에서 더 나을지 모르겠다) 조작하려는 대상 필드 / 메소드의 이름과 서명입니다.


2
리플렉션을 사용하면 개인 메소드를 테스트 할 수 있지만 사용 방법을 감지하는 데는 좋지 않습니다. 개인 메소드의 이름을 변경하면 테스트가 중단됩니다.
Richard

1
테스트는 그렇습니다. 그러나 이것은 내 관점에서 사소한 문제입니다. 물론 나는 패키지 가시성 (분리 폴더의 테스트 클래스와 동일한 패키지로)에 대한 아래 설명에 완전히 동의하지만 때로는 그렇지 않습니다. 정말 선택의 여지가 있습니다. 예를 들어, "좋은"방법을 적용하지 않는 엔터프라이즈에서 사명이 짧은 경우 (같은 패키지에없는 테스트 클래스 등), 작업중인 기존 코드를 리팩터링 할 시간이없는 경우 / 테스트, 이것은 여전히 ​​좋은 대안입니다.
kij

2

C #에서 일반적으로하는 일은 내 메서드가 개인용이 아닌 보호되도록하는 것입니다. 약간 덜 개인 액세스 수정 자이지만 테스트중인 클래스에서 상속되지 않는 모든 클래스에서 메소드를 숨 깁니다.

public class classUnderTest
{
   //this would normally be private
   protected void methodToTest()
   {
     //some code here
   }
}

classUnderTest에서 직접 상속되지 않은 클래스는 methodToTest가 존재한다는 것을 모릅니다. 내 테스트 코드 에서이 메소드에 대한 액세스를 제공하고 확장하는 특수 테스트 클래스를 만들 수 있습니다 ...

class TestingClass : classUnderTest
{
   public void methodToTest()
   {
     //this returns the method i would like to test
     return base.methodToTest();
   }
}

이 수업은 테스트 프로젝트에만 있습니다. 유일한 목적은이 단일 방법에 대한 액세스를 제공하는 것입니다. 그것은 대부분의 다른 수업이없는 장소에 접근 할 수있게 해줍니다.


4
protected는 공개 API의 일부이며 동일한 제한이 발생합니다 (변경 될 수없고 문서화되어야합니다 ...). C # internal에서는와 함께 사용할 수 있습니다 InternalsVisibleTo.
코드 InChaos

1

단위 테스트를 테스트중인 클래스의 내부 클래스에 배치하면 개인 메소드를 쉽게 테스트 할 수 있습니다. TestNG를 사용하면 유닛 테스트는 다음과 같이 @Test로 주석이 달린 공개 정적 내부 클래스 여야합니다.

@Test
public static class UserEditorTest {
    public void test_private_method() {
       assertEquals(new UserEditor().somePrivateMethod(), "success");
    }
}

내부 클래스이므로 private 메서드를 호출 할 수 있습니다.

내 테스트는 maven에서 실행되며 자동으로 이러한 테스트 사례를 찾습니다. 수업 하나만 시험하고 싶다면 할 수 있습니다

$ mvn test -Dtest=*UserEditorTest

출처 : http://www.ninthavenue.com.au/how-to-unit-test-private-methods


4
배포 된 패키지의 실제 코드에 테스트 코드 (및 추가 내부 클래스)를 포함시키는 것이 좋습니다.

1
테스트 클래스는 테스트하려는 클래스의 내부 클래스입니다. 이러한 내부 클래스를 배포하지 않으려면 패키지에서 * Test.class 파일을 삭제하면됩니다.
Roger Keays

1
나는이 옵션을 좋아하지 않지만 많은 사람들에게는 아마도 downvotes의 가치가없는 유효한 솔루션 일 것입니다.
Bill K

@RogerKeays : 기술적으로 배포를 수행 할 때 이러한 "내부 테스트"를 어떻게 제거합니까? 손으로 자르지 말아주세요.
gyorgyabraham

나는 그들을 제거하지 않습니다. 예. 런타임에 절대 실행되지 않는 코드를 배포합니다. 정말 나쁘다.
Roger Keays
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.