programing

junit & java : 비공개 메서드 테스트

luckcodes 2023. 1. 21. 10:21

junit & java : 비공개 메서드 테스트

JUnit은 우리 반에서 공개적인 방법만 테스트할 것입니다.(프라이빗, 프로텍트 등)이 아닌 것에 대해 junit 테스트를 실시하려면 어떻게 해야 합니까?

junit을 사용하지 않고 테스트할 수 있지만, junit의 표준방법이 무엇인지 궁금했습니다.

유닛 테스트에 대한 한 학파는 퍼블릭 메서드만 테스트할 수 있어야 한다고 말합니다.이는 퍼블릭 API만 유닛 테스트해야 하기 때문입니다.또한 이를 통해 퍼블릭 이외의 메서드에서 코드를 커버해야 하기 때문입니다.마일리지가 다를 수 있습니다.경우에 따라 다르기도 하고 그렇지 않을 수도 있습니다.

이와 같이 공개되지 않은 방법을 테스트하는 방법에는 몇 가지가 있습니다.

  • 유닛 테스트를 테스트하는 클래스와 동일한 패키지에 넣어 보호된 메서드 및 패키지 범위 메서드를 테스트할 수 있습니다.이것은 꽤 흔한 관행이다.
  • 테스트 대상 클래스의 서브클래스를 생성하여 퍼블릭으로 테스트하는 메서드를 덮어쓰고 이러한 오버라이드된 메서드가 super 키워드를 사용하여 원래 메서드를 호출함으로써 다른 패키지의 유닛테스트에서 보호된 메서드를 테스트할 수 있습니다.일반적으로 이 "테스트 서브 클래스"는 테스트를 수행하는 JUnit Test Case 클래스의 내부 클래스입니다.이게 좀 더 진부한 것 같아요. 하지만 제가 해냈어요.

이게 도움이 됐으면 좋겠다.

많은 유닛 테스트 문제와 마찬가지로 개인 방법을 테스트하는 것은 실제로는 설계상의 문제가 됩니다.사적인 방법을 시험하기 위해 어떤 까다로운 방법을 시도하기 보다는, 사적인 방법을 시험해 보고 싶을 때, 나는 잠시 시간을 내어 "공적인 방법을 통해 철저히 시험할 수 있도록 어떻게 설계할 필요가 있을까?"라고 자문한다.

그래도 안 될 경우 JUnitX는 개인 메서드를 테스트할 수 있지만 JUnit 3.8에서만 사용할 수 있다고 생각합니다.

JUnit 시험을 쓸 때, 당신은 미묘한 사고방식을 바꿔야 한다: "나는 이제 내 수업의 고객이다.즉, 프라이빗은 프라이빗이며 클라이언트에 표시되는 동작만 테스트합니다.

만약 그 방법이 정말로 비공개로 되어 있다면, 나는 단지 테스트만을 위해 그것을 보이게 하는 것은 디자인상의 결함이라고 생각한다.고객이 보는 것을 바탕으로 정확한 작동을 추론할 수 있어야 합니다.

이 글을 쓴 지 3년이 지난 지금, Java Reflection을 사용하여 조금 다른 방법으로 문제에 접근하기 시작했습니다.

이 더러운 작은 비밀은 JUnit에서 개인 메서드를 테스트하는 것과 마찬가지로 반영을 사용하여 테스트할 수 있다는 것입니다.마음껏 테스트해도 고객에게 공개되지 않습니다.

가장 간단한 해결책은 JUnit 테스트를 동일한 패키지(단, 다른 디렉토리)에 넣고 메서드에 기본(즉, 패키지-프라이빗) 가시성을 사용하는 것입니다.

또 다른 복잡한 접근방식은 반사를 사용하여 개인 메서드에 액세스하는 것입니다.

비교적 적은 수의 "공용" 진입점에 상당한 양의 논리가 묻혀 있다면 단일 책임 원칙을 위반하는 것일 수 있습니다.가능하면 코드를 여러 클래스로 리팩터링하여 최종적으로 테스트할 "퍼블릭" 메서드를 늘릴 수 있습니다.

여기 다른 사람들이 계속 당신에게 말하는 "아마도 이런 식으로 하면 안 될 것" 방법이 있습니다.하지만 이런 식으로 하는 데는 분명히 이유가 있다고 생각합니다.다음 코드는 개인 필드에 액세스하지만 개인 메서드의 코드는 거의 동일합니다.

public void testPrivateField() throws InterruptedException {
    Class<ClassWPrivateField> clazz = ClassWPrivateField.class;
    try {
        Field privateField = clazz.getDeclaredField("nameOfPrivateField");
        privateField.setAccessible(true); // This is the line
        // do stuff
    } catch(NoSuchFieldException nsfe) {
        nsfe.printStackTrace();
        fail();
    } catch(IllegalAccessException iae) {
        iae.printStackTrace();
        fail();
    }

}

Java 프로젝트에서는 거의 항상 Spring을 사용하고 있기 때문에 오브젝트는 의존성 주입용으로 구축되어 있습니다.이들은 애플리케이션 컨텍스트 내에서 조립된 퍼블릭인터페이스의 상당히 세밀한 구현이 되는 경향이 있습니다.이와 같이, 수업 자체가 충분히 작아서 문제가 되지 않기 때문에 개인 방법을 테스트할 필요가 거의 없습니다.

스프링을 사용하지 않을 때에도 저는 작고 단순한 오브젝트를 점점 더 큰 오브젝트로 조립하는 동일한 관행을 채택하는 경향이 있습니다.각 오브젝트는 비교적 단순하지만 집약된 오브젝트에 의해 복잡해집니다.

지금까지의 경험으로 볼 때, 프라이빗 메서드를 조합할 필요가 있는 것은, 학습하고 있는 것을 심플화할 수 있는(그리고, 또 심플하게 할 필요가 있는) 지표입니다.

그 때문에, 만약 당신이 여전히 필요성을 느낀다면:

  • 보호된 방법은 서브클래스로 테스트할 수 있다.
  • 패키지 개인 방법은 유닛 테스트를 동일한 패키지에 넣어 테스트할 수 있습니다.
  • 개인 메서드는 예를 들어 패키지 개인 팩토리 프록시 메서드를 제공하여 유닛 테스트를 수행할 수 있습니다.이상적이지 않지만 사적인 것은 사적인 것을 의미한다.

개인 메서드는 일반적으로 다른 공용 메서드를 통해서만 간접적으로 테스트할 수 있으므로 일반적으로 테스트하지 않습니다.시승 및 개인 방법을 만드는 경우 일반적으로 "추출 방법" 리팩터링의 결과이며 이미 간접적으로 테스트되고 있습니다.

논리가 많은 개인 메서드를 테스트하는 데 관심이 있는 경우 가장 현명한 방법은 해당 코드를 공용 메서드의 다른 클래스로 이동하는 것입니다.이 작업을 완료하면 이 코드를 사용한 이전 방법에서는 stub 또는 mock에 의해 제공되는 기능을 통해 테스트를 간소화할 수 있습니다.

저도 같은 문제에 부딪힌 적이 있는데, "사적으로 해야 한다면 아마 리팩터링해야 할 것"이라는 말은 제게 맞지 않습니다.

클래스 내부에서 어떤 방식으로든 분리할 수 있는 기능이 있다고 가정합니다.예를 들어 다음과 같은 것이 있다고 가정합니다.

public class HolderOfSomeStrings{

    private List<String> internal_values;

    public List<String> get()
    {
       List<String> out = new ArrayList<String>();
       for (String s:internal_values)
       { 
           out.add(process(s));
       }
      return get;
    }

   private static String process(String input)
   {
     //do something complicated here that other classes shouldn't be interested in
    }

}

여기서 요점은 프로세스를 공개하거나 최소한 보호하거나 자체 유틸리티 클래스에 넣어야 한다는 것입니다.그러나 Holder Of Some Strings의 내부 논리 같은 것이라면, 이것이 올바른 것인지 전혀 확실하지 않습니다.비공개적인 방법으로 코드를 보다 명확하게 표시할 필요가 있다고 생각합니다.

Andy Hunt의 생각을 빌리자면, 당신의 개인적인 방법들조차도 당신이 관심을 가지고 있는 몇 가지 부작용이 있을 것이다.즉, 공개 메서드에서 호출하여 오브젝트 상태를 변경하는 흥미로운 작업을 수행해야 합니다.상태 변화를 테스트합니다.

public method pubMethod와 private method privMethod가 있다고 가정합니다.pubMethod를 호출하면 privMethod를 호출하여 작업을 수행합니다(String을 해석하는 경우도 있습니다).pubMethod는 이 해석된 문자열을 사용하여 멤버 변수의 값을 설정하거나 자신의 반환값에 영향을 줍니다.pubMethod의 반환값 또는 멤버 변수에 대한 원하는 영향을 관찰하여 테스트합니다(접근자를 사용하여 원하는 값을 얻을 수 있습니다).

DP4j Jar

개인 방법을 테스트하기 위해서는 모든 답변에서 성찰과 지적된 사항을 사용해야 합니다.

Dp4j jar의 도움으로 이 작업이 간소화되었습니다.

  • Dp4j는 코드를 분석하여 자동으로 Reflection API 코드를 생성합니다.

  • CLASSPATH에 dp4j.jar를 추가합니다.

  • Dp4j.jar에는 Annotation Processor가 포함되어 있습니다.이 프로세서는 @Test JUnit 주석으로 주석을 붙인 코드 내의 메서드를 찾습니다.

  • Dp4j는 이러한 메서드의 코드를 분석하여 프라이빗 메서드에 부정적으로 접근하고 있는 것을 발견하면 비활성 프라이빗 메서드 참조를 Java의 Reflection API를 사용하는 동등한 코드로 대체합니다.

자세한 내용은 이쪽

JUnit 대신 TestNG를 사용할 수 있습니다.이것은, 방식이 프라이빗이든 퍼블릭이든 상관없습니다.

위와 같이 리플렉션(reflection)을 사용하여 개인 메서드를 테스트합니다.만약 TDD를 따르고 있다면, TDD는 나중에 놀랄 일이 없다는 것을 의미하기 때문에 사설 방식을 테스트해야 합니다.그러므로 어떤 이는 그의 비밀들을 시험하기 위한 공개적인 방법을 끝낼 때까지 기다려서는 안 된다.이를 통해 재계수를 고려할 때 보다 세밀한 회귀 테스트가 가능합니다.

"Private Accessor"를 찾습니다.호출"을 클릭합니다.내 코드는 "junitx.util"에서 가져오지만 어디서 왔는지 알 수 없습니다.

모든 투고에 동의하며, 아마도 당신은 리팩터링해야 할 것이고, 아마도 공개를 통해서만 비공개 테스트를 하지 말아야 할 것이다. 단지 그것에 대해 다른 생각을 덧붙이고 싶을 뿐이다.

수업 자체를 방법이 아니라 "단위"로 생각하십시오.클래스를 테스트하고 있으며 공개 메서드의 호출 방법에 관계없이 유효한 상태를 유지할 수 있습니다.

프라이빗 메서드를 호출하면 캡슐화가 파기되어 테스트가 실제로 무효가 될 수 있습니다.

이것의 일부로서, 「폴리글로트 프로그래밍」의 문제 전체에 대해서, 모두가 어떻게 생각하고 있는지는 모르겠지만, 그루비 테스트는 Junit에서 실행할 수 있고, 공개/비공개적인 문제 전체를 무시합니다.덧붙여서, 그것은 공식적으로 "버그"로 분류되었지만, 그들이 그것을 고치려고 했을 때, 그것은 원래대로 되돌려 놓을 정도로 폭풍우가 몰아쳤다.

코드를 클라이언트의 관점에서 테스트해야 한다는 @duffymo의 의견에 전적으로 동의합니다(그는 그렇게 생각하지 않는다고 하지만).그러나, 이러한 관점에서 볼 때, 개인과 타인은 다른 의미를 가지고 있다.프라이빗 메서드의 클라이언트는 클래스 자체이기 때문에 외부(public/package protected) API를 통해 테스트하는 것이 좋습니다.단, 외부 클라이언트에는 보호 멤버와 패키지 보호 멤버가 있기 때문에 소유 클래스를 상속받거나 같은 패키지에 있는 가짜로 테스트합니다.

언급URL : https://stackoverflow.com/questions/440786/junit-java-testing-non-public-methods