어떻게 테스트 가능 있는 클래스 전용 방법,분야 또는 내부 클래스?

StackOverflow https://stackoverflow.com/questions/34571

  •  09-06-2019
  •  | 
  •  

문제

어떻게 유닛 테스트(사용하여 버전)가 있는 클래스고 내부 개방하거나 중첩된 클래스가?또는 기능을 하는 개인으로써 내부 링크 (static C/C++)또는 프라이빗(익명)namespace?

그것은 나쁜 것에 대한 액세스 권한을 변경하려면 수정을 위한 방법 또는 기능을 실행할 수 있는 테스트입니다.

도움이 되었습니까?

해결책

업데이트:

으로 약 10 년 후에는 아마도 최고의 방법을 테스트하는 전용 방법,또는 액세스 할 수없는 회원가를 통해 @Jailbreak매니폴드 framework.

@Jailbreak Foo foo = new Foo();
// Direct, *type-safe* access to *all* foo's members
foo.privateMethod(x, y, z);
foo.privateField = value;

이 방법은 당신의 코드 유형-안전하고 읽을 수 있습니다.없는 디자인,타협 없이 과도한 노출을 예방 하는 방법과 필드의 이익을 위해 테스트합니다.

이 있는 경우 약간의 레거시 Java 응용 프로그램,당신은 허용되지 않의 가시성을 변경하는 방법,이를 테스트하는 가장 좋은 방법인 방법을 사용하는 것입 반사.

내부적으로 우리가 사용하는 조 get/set privateprivate static 변수뿐만 아니라 invoke privateprivate static 방법이 있습니다.다음과 같은 패턴을 것입니다 당신은 거의 아무것도와 관련된 개인 방법과 필드가 있습니다.의 과정을 변경할 수 없습니다 private static final 변수를 반영합니다.

Method method = TargetClass.getDeclaredMethod(methodName, argClasses);
method.setAccessible(true);
return method.invoke(targetObject, argObjects);

과에 대한 분야:

Field field = TargetClass.getDeclaredField(fieldName);
field.setAccessible(true);
field.set(object, value);

Notes:
1. TargetClass.getDeclaredMethod(methodName, argClasses) 할 수 있으로 보고 private 방법이 있습니다.같은 일이 적용됩 getDeclaredField.
2.이 setAccessible(true) 데 필요한 놀니다.

다른 팁

를 테스트하는 가장 좋은 방법인 방법을 통해 다른 공용 방법입니다.이렇게 할 수 없으면,그때는 다음 조건 중 하나이다:

  1. 개인 방법은 죽은 코드
  2. 거기 디자인 냄새가 근처에는 클래스 테스트
  3. 하는 방법 당신은 노력하고 테스트하지 않아야 될 것인

을 때 저는 방법 클래스에서는 충분히 복잡는 나는 필요성을 느끼를 테스트하는 개인 방법을 직접는 냄새가 코드:내가 너무 복잡합니다.

나의 일반적인 접근 방식을 해결 같은 문제에게 소개하는 새로운 클래스를 포함하는 흥미있는 비트입니다.종종 이 방법 및 분야와 상호작용,그리고 어쩌면 다른 방법이나 두 추출할 수 있습에 새로운 클래스입니다.

새로운 클래스를 노출하한 방법으로'공공',그래서 그들은 접근을 위한 단위 테스트입니다.새로운 오래된 클래스는 지금은 모두 더 간단한 원래보다 클래스는 나를 위해 좋은 것입니다(저는 필요한 물건을 유지하는 간단하나 나는 길을 잃!).

참고하는 그런 얘기가 아닙니다 사람들은 만들기 클래스를 사용하지 않고 그들의 뇌!여기에 포인트를 사용하여 힘의 단위 테스트를 찾을 수 있도록 좋은 새로운 클래스입니다.

반사 이를 위해 Java 과거에는,그리고 내 생각에 그것은 큰 실수입니다.

엄밀히 말하면,당신 쓰고 있는 단위 테스트가 직접 테스트는 개인 방법입니다.당신 테스트는 공용 계약이 해당 클래스가 다른 객체와;당신이해야 한 적이 직접 테스트 객체의 내부.는 경우 다른 개발자를 만들고 싶은 작은 내부를 변경하는 클래스에 영향을 미치지 않는 클래스가 공용 계약,그/그녀는 다음을 수정할 수의 반영을 기반으로 테스트가 작동하는지 확인하십시오.이렇게 하면 내내 반복적으로 프로젝트,단위 테스트는 다음을 멈추고 유용한 측정의 코드 건강,고 시작을 방해가 될을 개발,그리고 짜증을 개발 합니다.

내가 추천하고 대신 사용하는 코드 검사 도구와 같은 Cobertura 을 보장하기 위해,단위 테스트를 작성을 제공해의 범위는 코드에서 개인 방법입니다.그런 식으로,당신은 간접적으로 테스트가 무엇인 방법을 하고 유지하는 높은 수준의 민첩성이 있습니다.

이 문서에서: 테스트는 개인 방법을 JUnit 및 SuiteRunner (빌 Venners),당신은 기본적으로 4 가지 옵션:

  • 시험하지 않인 방법입니다.
  • 제공 방법은 패키지 액세스입니다.
  • 사용 중첩된 테스트 클래스가 있습니다.
  • 사용 반영합니다.

일반적으로 단위 테스트가 의도하는 운동의 공용 인터페이스 클래스 또는 단위입니다.따라서,개인 방법을 구현하는 세부사항 기대하지 않을 것입하 테스트는 명시적으로 합니다.

두 가지 예의는 내가 원하는 것을 테스트하는 개인 방법:

  1. 암호 해독 루틴 -지 않았지 하려는 그들이 눈에 보이는 사람만 의 테스트,다른 사람 들을 사용하여 암호 해독.하지만 그들은 내장 코드의 복잡한 고 필요를 항상 작업(명백한 예외로 반영하는 데 사용할 수 있는 뷰어설 방법에 대부분의 경우, SecurityManager 도록 구성되지 않은 이를 방지하기 위해).
  2. SDK 를 만들기 커뮤니티 소비입니다.여기에 공공에 걸리 전체적으로 다른 의미를 이 는 코드는 전 세계를 볼 수 있습니다 (단지 내부을 내 응용 프로그램).나는 코드로 개인 메지 않는 경우 원 SDK 사용자에게 표시-나가 지 냄새가 코드,단순히 는 방법으로 SDK 는 프로그래밍을 작동합니다.하의 물론 내가 여전히 필요하 테스트 전용 방법,그리고 그들은 어디서 의 기능을 내 SDK 제 삶입니다.

나는 아이디어를 이해의 테스트는"계약".그러나 나는 하나 볼 수 없 옹호할 수 있는 실제로지 코드를 테스트-귀하의 주행거리를 달라질 수 있습니다.

그래서 나의 교환을 포함한 복잡 JUnits 으로,반사 보다는 오히려 손상 내 security&SDK.

개인 방법에 따라 그리스도인이라 불리는 공공 방법,그래서 입력을 당신의 방법을 공개해야 한한 테스트는 개인 방법이라고 하는 사람들에 의해 공중 방법이 있습니다.면 방법이 실패하면,그 수 있는 실패한 것인 방법입니다.

다른 방법이 사용을 변경하는 것 전용 방법은 패키지는 개인 또는 보호되는 그 보완으로 @VisibleForTesting 주석의 구글은 구아바 라이브러리입니다.

이것은 말할 사람이 방법을 사용하는 방법을 주의하지 않는 직접 심지어 패키지입니다.또한 테스트 등에서 하지 않는 동일한 패키지 물리적으로, 지만,동일 패키지 테스트 폴더에 있습니다.

는 경우,예를 들어,방법을 테스트할에 src/main/java/mypackage/MyClass.java 다음 테스트 전화를 걸어야에 배치 src/test/java/mypackage/MyClassTest.java.는 방법은,당신은에 액세스하는 테스트 방법을 테스트에서 클래스입니다.

테스트 레거시 코드와 크고 독특한 클래스를,그것은 종종 매우 도움이 될 수 있을 테스트하는 한 개인(또는 공개)방법을 쓰고 있어요 지금.

junitx.util.PrivateAccessor-package Java.많은 도움이너에 대한 액세스하는 전용 방법과 프라이빗 필드가 있습니다.

import junitx.util.PrivateAccessor;

PrivateAccessor.setField(myObjectReference, "myCrucialButHardToReachPrivateField", myNewValue);
PrivateAccessor.invoke(myObjectReference, "privateMethodName", java.lang.Class[] parameterTypes, java.lang.Object[] args);

을 시도하는 데 Cem Catikkas' 솔루션을 사용하여 반영 Java,나는 말을 그었다 더 우아한 솔루션을 보다 나는 여기에 설명되어 있습니다.그러나,당신이에 대한 대안을 찾고 리플렉션을 사용하여,그리고 소스에 액세스 할 수 있습신 테스트,이것은 여전히있는 옵션입니다.

가 가능한 공로 테스트에서 개인의 클래스 메소드,특히 테스트 기반의 개발, 당신하고 싶은 디자인 작은 테스트를 작성하기 전에 모든 코드입니다.

성 테스트를 액세스하는 개인 회원들과 방법을 테스트할 수 있습의 지역 코드가 어려운 목표와 구체적으로 액세스를 공개 방법이 있습니다.는 경우에는 공용 방법에는 여러 단계에 참여할 수 있습으로 구성의 여러 가지 방법 개,테스트될 수 있습니다.

장점:

  • 할 수 있는 테스트를 세분화

단점:

  • 테스트 코드에 있어야 합니다 동 파일 소스 코드를 수 있는 더욱 유지하기 어려울
  • 마찬가지로 가진.클래스 출력 파일을 해야 합니다 그들은 내에서 유지와 같은 패키지 선언에 소스 코드

그러나,경우 지속적인 테스트를 필요로 이 방법이 될 수 있는 신호의 개인 방법을 추출해야 될 수 있는 테스트에서 전통,공공의 방법입니다.

여기에는 복잡한의 예는 어떻게 작동:

// Import statements and package declarations

public class ClassToTest
{
    private int decrement(int toDecrement) {
        toDecrement--;
        return toDecrement;
    }

    // Constructor and the rest of the class

    public static class StaticInnerTest extends TestCase
    {
        public StaticInnerTest(){
            super();
        }

        public void testDecrement(){
            int number = 10;
            ClassToTest toTest= new ClassToTest();
            int decremented = toTest.decrement(number);
            assertEquals(9, decremented);
        }

        public static void main(String[] args) {
            junit.textui.TestRunner.run(StaticInnerTest.class);
        }
    }
}

내부 클래스를 컴파일할 ClassToTest$StaticInnerTest.

도 참조하십시오: Java 팁 106:정적 내부 클래스에 대한 재미있고 이익

으로 다른 사람이 말하기를...시험하지 않인 방법을 직접 있습니다.여기에 몇 가지 생각이 있다:

  1. 모든 방법은 작은 초점을 맞추(을 쉽게 테스트,쉽게 찾을 수 있 무엇이 잘못)
  2. 코드를 사용 검사 도구입니다.나 Cobertura (오 행복한 날,같은 새로운 버전입니다!)

실행 코드 범위에 대한 단위 테스트.당신이 볼 수 있는 방법은 완전히 테스트에 추가 테스트를 얻을 적용니다.에 대한 목표를 100%코드 검사,하지만 당신은 아마 당신은 그것을 얻지 않을 것이다.

개인 방법은 소비에 의해 공중다.그렇지 않으면,그들은 죽은 코드입니다.그런 이유로 당신 테스트 방법을 공개,주장하는 예상된 결과의 공공 방법 및 이에 따라,개인 방법 그것을 소모합니다.

테스트는 개인 방법에 의해 테스트해야 디버깅을 실행하기 전에 장치 테스트에서 공개 방법이 있습니다.

그들은 또한 디버깅을 사용하여 테스트 중심의 개발,디버깅 단위 테스트를 때까지의 모든 주장들을 만났습니다.

나 개인적으로 믿고 그것은 더 나은 클래스를 만들어 사용하 TDD;을 만드는 방법을 공개 명세서를,그때를 생성하여 단위 테스트 모든 의 주장에서 정의된 미래 예상된 결과의 방법을 결정하기 전에 당신은 코드습니다.이 방법은,당신이 가지 않는 잘못된 경로를 아래로 만드는 단위 테스트션에 맞는 결과입니다.귀하의 클래스가 다음 강력하고 요구 사항을 충족하면 귀하의 모든 단위 테스트를 통과합니다.

Spring Framework 테스트할 수 있습니다 개인 방법을 이 방법을 사용:

ReflectionTestUtils.invokeMethod()

예를 들어:

ReflectionTestUtils.invokeMethod(TestClazz, "createTest", "input data");

를 사용하는 경우 봄, ReflectionTestUtils 제공하는 몇 가지 유용한 도구는 여기에는 최소한의 노력으로.예를 들어,설정하는 모형에 전원없이 강제되고 추가 바람직하지 않은 공공 setter:

ReflectionTestUtils.setField(theClass, "theUnsettableField", theMockObject);

는 경우에 당신을 테스트하기 위해 노력하고 기존하고 있는 코드를 꺼려하거나 변경할 수 없습,리플렉션은 좋은 선택이 될 것입니다.

하드 디스크 드라이브의 디자인은 여전히 유연하고,당신이 가지고 복잡한 개인하는 방법을 테스트하려는 별도로,나는 당신이 그것을 밖으로 끌어로 별도의 클래스고 테스트하는 클래스 별도로.이지 않을 변경해야 공용 인터페이스는 원래의 클래스수 내부적으로 인스턴스를 만들의 도우미 클래스고 도우미를 호출 방법입니다.

테스트할 경우 어렵 오류 조건에서 오는 도우미가 방법,할 수 있습니다.추출물 인터페이스에서 도우미 클래스고,추가 공용 getter 및 세터 본래 등을 주입하기 도우미 클래스(이용해 인터페이스),그리고 다음 주는 모의 버전을 도우미 클래스로 원래의 클래스를 테스트하는 방법 원본 클래스에 응답하는 예외에서 도우미입니다.이 방법은 또한 도움을 테스트하려면 원래 없는 또한 테스트를 도우미 클래스입니다.

테스트 방법 개 나누기의 캡슐화에 당신의 클래스 때문에 모든 시간을 변경 내부 구현을 끊은 클라이언트 코드(이 경우,테스트).

그래서 시험하지 않인 방법입니다.

대답터 JUnit.org FAQ 페이지:

하지만해야 하는 경우에는...

당신이 사용하는 JDK1.3 이상을 사용할 수 있습니다,반사를 파괴하기 액세스 제어 메커니즘의 도움으로 PrivilegedAccessor.에 대한 자세한 내용은 그것을 사용하는 방법,읽기 이 문서.

당신이 사용하는 JDK1.6 이상에서 당신은 주석 테스트 @테스트를 사용할 수 있습니다 Dp4j 주입 반사에서 귀하의 테스트 방법이 있습니다.대 을 사용하는 방법에 대한 세부 사항을 참조하십시오 이 테스트는 스크립트.

P.S.나의 주된 원 Dp4j, 요청, 는 경우에 당신은 도움이 필요합니다.:)

를 테스트하려는 경우에는 개인을 위해서는 기존 응용 프로그램을 변경할 수 없습니다 코드,옵션 중 하나에 대한 자바 jMockit, 는 것을 만들 수 있도록 조롱하는 개체는 경우에도 그들이 전용 클래스입니다.

는 경향이 없을 테스트하는 개인 방법입니다.가 중요한 부분입니다.개인적으로,나는 당신을 믿어야만을 테스트하는 공개적으로 표시되는 인터페이스(및을 포함하는 보호 및 내부 방법).

를 사용하는 경우 JUnit 봐야 junit-애드온.그것은 능력을 무시하 Java 보안 모델이고 빠르고 안전한 액세스를 제공합 방법과 특성이 있습니다.

전용 방법은 내에서 액세스할 수 있습니다.그래서 방법은 없을 테스트하는"개인"방법의 대상 클래스에서 어떤 테스트 클래스가 있습니다.방법은 수행할 수 있는 단위 테스트를 수동으로 또는 변경할 수 있습 방법에서는"개인"을"보호".

그리고 보호되는 방법만 액세스할 수 있습니다 이내에는 동일한 패키지는 클래스가 정의됩니다.그래서 테스트,보호방법의 대상 클래스를 의미 우리가 정의할 필요가 테스트에서 클래스와 같은 패키지 대상 클래스입니다.

는 경우 위의 모든지 않는 귀하의 요구 사항에 맞,사용 반사 방법 액세스하는 전용 방법입니다.

나는 좋은 당신 refactoring 의 코드 작은 비트입니다.할 때 생각하기 시작해야에 대해 사용하는 반사 또는 다른 종류의 물건에 대한 테스트 코드는 뭔가 잘못된 것으로 당신의 코드입니다.

당신이 언급한 다양한 유형의 문제입니다.시작하자 프라이빗 필드가 있습니다.의 경우에는 프라이빗 필드가 추가로 새로운 생성자 및 주입된 필드를 추가합니다.대신 이:

public class ClassToTest {

    private final String first = "first";
    private final List<String> second = new ArrayList<>();
    ...
}

내가 이것을 사용:

public class ClassToTest {

    private final String first;
    private final List<String> second;

    public ClassToTest() {
        this("first", new ArrayList<>());
    }

    public ClassToTest(final String first, final List<String> second) {
        this.first = first;
        this.second = second;
    }
    ...
}

이 문제가 없는 심지어 일부 레거시 코드입니다.오래된 코드를 사용하여 빈 생성자,그리고 당신이 저를 요구하는 경우,refactor 코드 보이는 것입 청소기,그리고 당신은 수있을 것을 주입하기에 필요한 값을 시험없이 반영합니다.

지금에 대한 개인 방법입니다.나 개인적인 경험을 때 당신은 스텁 전용 방법에 대한 테스트,그 다음 방법은 할 수 없는 클래스입니다.일반적인 패턴 이 경우에는 것을 그것은에서와 같은 인터페이스 Callable 그리고 당신은이 통과에서는 인터페이스에서 생성자(으로 여러 생성자릭):

public ClassToTest() {
    this(...);
}

public ClassToTest(final Callable<T> privateMethodLogic) {
    this.privateMethodLogic = privateMethodLogic;
}

대부분의 모든 것을 내가 쓴 것처럼 보인의 종속성을 주입에 패턴이 있습니다.내 개인적인 경험에서 그것은 정말 유용하 테스트하는 동안,그리고 나는 생각한 그의 이런 종류의 코드입 청소기고 유지를 쉽게 할 수 있습니다.나는 같은 말에 대한 중첩된 클래스입니다.는 경우에는 중첩된 클래스에 포함되어 무거운 논리는 것이 나면 이동으로 패키지는 전용 클래스가 주입된 그로스 클래스를 필요로 그것.

또한 여러 가지 다른 디자인 패턴 이는 내가 사용하는 동안 refactoring 및 유지 레거시 코드,하지만 모두가에 따라 달라의 경우 코드를 테스트합니다.리플렉션을 사용하여 대부분이 문제가되지 않습니다,하지만 있을 때는 기업 응용 프로그램은 크게 테스트하고 테스트를 실행하기 전에 모든 배포하는 것이 정말로 느린(그냥 성가시고 내가 좋아하지 않는 종류의 물건).

또한 세터 주입이지만,없을 사용하는 것이 좋습니다.내가 더 붙으로 생성자를 초기화하면 모든 것이 정말 필요한 가능성을 떠나 주입에 필요한 종속성입니다.

으로 많은 위의 제안,좋은 방법은 테스트를 통해 공용 인터페이스가 있습니다.

이렇게 하면 그것은 좋은 아이디어를 사용하는 코드 검사 도구(번)확인하려면 개인 방법은 실제로 실행에서 당신의 테스트합니다.

여기에는 나의 일반적인 기능을 시험 프라이빗 필드:

protected <F> F getPrivateField(String fieldName, Object obj)
    throws NoSuchFieldException, IllegalAccessException {
    Field field =
        obj.getClass().getDeclaredField(fieldName);

    field.setAccessible(true);
    return (F)field.get(obj);
}

아래 참조하시기 바랍에 대한 예;

다음과 같은 수입 문이 추가되어야:

import org.powermock.reflect.Whitebox;

지금 당신이 직접 할 수 있습니다 통과하는 개체는 개인 방법,메서드 이름이라는 것이다 추가 매개 변수는 아래와 같습니다.

Whitebox.invokeMethod(obj, "privateMethod", "param1");

오늘 밀어는 자바 라이브러리하는 데 도움이 테스트는 개인 방법과 필드가 있습니다.로 설계되었습니다드에 마음,하지만 그것은 정말 위해 사용될 모든 Java 프로젝트입니다.

을 얻은 경우에는 코드는 전용 방법 또는 분야 또는 생성자를 사용할 수 있습니다 BoundBox.그것은 정확히 무엇을 찾고 있습니다.여기에는 아래의 예는 테스트를 액세스하는 두 개의 필드로 활동하 테스트:

@UiThreadTest
public void testCompute() {

    // Given
    boundBoxOfMainActivity = new BoundBoxOfMainActivity(getActivity());

    // When
    boundBoxOfMainActivity.boundBox_getButtonMain().performClick();

    // Then
    assertEquals("42", boundBoxOfMainActivity.boundBox_getTextViewMain().getText());
}

BoundBox 을 쉽게 테스트립/보호된 필드 방법과 생성자입니다.액세스할 수 있습니다 심지어는 물건에 의해 숨겨진 상속입니다.실제로,BoundBox 나누기를 캡슐화합니다.그것은 당신에게 서비스를 통해 반사, 모든 확인은 컴파일때 정해진다.

그것은 이상적 테스트를 위해 일부 레거시 코드입니다.신중하게 그것을 사용하세요.;)

https://github.com/stephanenicolas/boundbox

첫째,내가 이 질문을 던져 아웃:왜 귀하의 개인 회원들에게 필요한 절연 테스트?는 자들은 복잡한 제공하고,이러한 복잡한 행동으로 할 필요가 테스트에서 떨어져 공용 표면?그것의 단위 테스트,하지'라인의 코드'테스트합니다.지 않는 땀을 작은 물건입니다.

그들은 큰 충분히 이러한 개인 회원들은 각각 장치에서 큰 복잡성--리팩터링을 고려한 개인 회원의 이 클래스입니다.

는 경우 refactoring 이 부적절하거나 불가능할 수 있는 전략을 사용 패턴을 바꾸기에 액세스하여 이러한 개인원 기능/회원의 경우 클래스에서는 단위 테스트?아래 단위 테스트,전략 제공 할 것이라 추가 유효성,그러나 릴리스 빌드에서 그것은 간단한 의미합니다.

나는 최근에 문제가 없었고 쓴 약이라는 도구를 Picklock, 는지의 문제를 사용하여 명시적으로 Java 반사 API,두 가지 예:

전화 방법,예를 들어, private void method(String s) -에 의해 반사 Java

Method method = targetClass.getDeclaredMethod("method", String.class);
method.setAccessible(true);
return method.invoke(targetObject, "mystring");

전화 방법,예를 들어, private void method(String s) -의 Picklock

interface Accessible {
  void method(String s);
}

...
Accessible a = ObjectAccess.unlock(targetObject).features(Accessible.class);
a.method("mystring");

설정의 필드는,예를 들어, private BigInteger amount; -에 의해 반사 Java

Field field = targetClass.getDeclaredField("amount");
field.setAccessible(true);
field.set(object, BigInteger.valueOf(42));

설정의 필드는,예를 들어, private BigInteger amount; -의 Picklock

interface Accessible {
  void setAmount(BigInteger amount);
}

...
Accessible a = ObjectAccess.unlock(targetObject).features(Accessible.class);
a.setAmount(BigInteger.valueOf(42));

Java 난 사용 반사, 때문에,저는 좋아하지 않을 바꾸는 생각이스 패키지에 선언된 방법만을 위해의 테스트합니다.그러나,일반적으로 그냥 테스트 방법을 공개해야 하는지도 확인인 방법을 제대로 작동하는지 확인합니다.

사용할 수 없습니다 리플렉션을 전용 방법이외에서 소유자 등 개인정 영향을 미친 반도

이것은 사실이 아닙니다.당신이 가장 확실 수 있습에 언급된 것과 같이 Cem Catikkas 의 응답.

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