【问题标题】:JUnit Testing: invoking parent class protected methodJUnit 测试:调用父类受保护的方法
【发布时间】:2026-01-20 23:00:01
【问题描述】:

这是我的类层次结构

abstract Class A
{
   int i = 0;
   protected init (String param1, String param2)
   {
       //do lots of common things based on param1, param2 and save data in i
   } 
}

然后有 3-4 个类实现了 A 即

Class B extends A
{
    public B ()
    {
        super ();
    }

    public void performSomeAction ()
    {
        init (param1_specific_to_class_B, param2_specific_to_class_B); //calling parent class method 
        //do rest of teh random things
    }
}

现在,我正在尝试为 A 类编写 JUnit 测试。基本上我想测试 init 方法所做的任何事情是否准确。我试过这个

Class clas = A.class;
B b = new B();

Method A_init;
A_init = clas.getDeclaredMethod("init", String.class, String.class);
A_init.invoke(b, param1_specific_to_class_B, param2_specific_to_class_B);

但它不工作,我得到以下异常

java.lang.IllegalAccessException: Class test.package.subpackage.ATest can not access a member of class package.subpackage.A with modifiers "protected"
    at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:65)
    at java.lang.reflect.Method.invoke(Method.java:578)
    at test.package.subpackage.ATest.initTest(ATest.java:49)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:585)

【问题讨论】:

    标签: java reflection methods junit protected


    【解决方案1】:

    一个类的protected 成员不是另一个包中的一个类的visible,除非它是前者的子类。

    但是,在您的应用程序中,类 test.package.subpackage.ATest 试图从 package.subpackage 中的另一个类访问受保护的方法,这会导致访问冲突。

    尝试将ATest 类放置到AATest 属于同一个包的位置。

    【讨论】:

      【解决方案2】:

      您可以将测试类与被测试类放在同一个包中。所以测试类将可以访问受保护的方法。

      另一个更优雅的选项是使受保护的方法可访问:

      A_init = clas.getDeclaredMethod("init", String.class, String.class);
      A_init.setAccessible(true);
      A_init.invoke(b, param1_specific_to_class_B, param2_specific_to_class_B);
      

      希望对你有帮助。

      【讨论】:

      • 将类移动到该包不是一种选择,因为我们有 2 个不同的包层次结构(项目类在 src 文件夹中,测试在 test folderR 中)。感谢setAccessible(true)。这就是我一直在寻找的
      • 与 Maven 项目一样,您有一个 src 和一个 test 文件夹,但不同的文件夹不一定意味着不同的包。
      【解决方案3】:

      也许我遗漏了一些东西,但是,由于 B 没有覆盖 A.init() (至少,从我所见)你可以去

      B b = new B()
      b.init(param1_specific_to_class_B, param2_specific_to_class_B)
      

      如果 B 覆盖 init(),

      1. 将所有内容放在同一个包中(与其他人发布的一样)
      2. 如果无法或不希望更改包,您可能必须向 B 添加一个特殊方法,该方法调用 super.init()。我会将其命名为 callSuperInitForUnitTests() 之类的名称,并在 cmets 中明确说明这是一种用于单元测试的解决方法。

      【讨论】:

      • 我知道 B 在扩展 A。关键是如果你想测试 A.init,并且 B 没有覆盖 A.init,你可以调用 b.init()。否则,这些是一些可能的解决方法:将文件移动到同一个包,设置可访问,或通过中间调用 A.init。
      【解决方案4】:

      或者在您的 TestClass 中创建 A 的子类并通过公共方法公开受保护的方法:

      public class TestClass extends TestCase(){
      
         // ...
      
         private class AChild extends A{
            public exposeInit (String param1, String param2){
               super.init(param1, param2);
            }
         }
      }
      

      现在您可以通过测试 AChild 类的 init 来测试 A 类的 init。

      我有类似的问题,但我们通过将设计改为组合而不是继承来解决它。

      【讨论】: