使用 Java 反射访问测试用例中的受保护方法

Posted

技术标签:

【中文标题】使用 Java 反射访问测试用例中的受保护方法【英文标题】:Accessing protected method in test case using Java Reflection 【发布时间】:2011-06-07 13:03:07 【问题描述】:

我正在尝试使用 Java 反射获取和调用驻留在不同类和不同包中的受保护方法。

包含受保护方法的类:

package com.myapp;

public class MyServiceImpl 

   protected List<String> retrieveItems(String status) 
         // Implementation
   

调用类:

package xxx.myapp.tests;

import com.myapp.MyServiceImpl;

public class MyTestCase 

    List<String> items;

    public void setUp() throws Exception 

         MyServiceImpl service = new MyServiceImpl();
         Class clazz = service.getClass();

         // Fails at the next line:
         Method retrieveItems = clazz.getDeclaredMethod("retrieveItems");

         // How to invoke the method and return List<String> items?
         // tried this but it fails?
         retrieveItems.invoke(clazz, "S");
    

编译器抛出这个异常:

java.lang.NoSuchMethodException: com.myapp.MyServiceImpl.retrieveItems()

【问题讨论】:

【参考方案1】:

如果您将测试用例放在同一个包中(com.myapp 而不是com.myapp.tests),他们将可以访问 protected(和 default 包级别)成员。

那你可以直接拨打service.retrieveMembers(status)

如果您试图将源代码与测试分开,通常最好使用不同的源目录(例如src 目录和test 目录)。

【讨论】:

如果测试在同一个包中,那么你编译的类文件很可能会在同一个目标目录中,这会使任何部署文件与一堆测试类文件膨胀,除非你去通过您拥有的每个包/项目并为目标目录定义排除规则。在我看来不是一个好主意。 这就是您使用不同的源目录进行测试的原因。这是 maven 等工具的标准工作方式。 是的,我误解了你的帖子。我明白你现在在说什么了,谢谢!【参考方案2】:

您应该在调用方法中使用创建对象的链接而不是类的链接,并使用 Method.setAccessible(true) 调用来解锁访问:

public void setUp() throws Exception 
    MyServiceImpl service = new MyServiceImpl();
    Class<?> clazz = service.getClass();
    Method retrieveItems = clazz.getDeclaredMethod("retrieveItems", String.class);
    retrieveItems.setAccessible(true);
    items = (List<String>)retrieveItems.invoke(service, "S");

【讨论】:

【参考方案3】:

不需要反射或继承:

将您的MyTestCase 放在package com.myapp 下,因为“protected”范围也是“package”。 那么MyTestCase就可以访问MyServiceImpl的protected方法了。

【讨论】:

如果测试在同一个包中,那么你编译的类文件很可能会在同一个目标目录中,这会使任何部署文件与一堆测试类文件膨胀,除非你去通过您拥有的每个包/项目并为目标目录定义排除规则。在我看来这不是一个好主意。 @SandySimonton,可以实现。我们可以使我们的项目结构如下(maven):src-&gt;main-&gt;java 包含所有代码。 src-&gt;test-&gt;java 包含所有测试。【参考方案4】:

为什么不简单地创建一个可以访问受保护方法的派生类,而不是使用那些棘手的反射东西?

更多想法请见Is it bad practice to use Reflection in Unit testing?。

【讨论】:

-1 -- 它不回答根本问题。在 Java 中如何通过反射访问受保护的方法? @BrainSlugs83 重新阅读问题的标题。 OP 实际上想测试一些代码,但错误地认为他们需要使用反射。他们有一个 XY 问题。我的回答是针对他们真正的问题,而不是他们提出的解决方案。 -1 -- “我正在尝试使用 Java 反射获取和调用驻留在不同类和不同包中的受保护方法。”这就是意图;你怎么知道这不像学校作业?似乎告诉他创建一个派生类(这会导致更多的代码行)就像用“使用开罐器”来回答“我想用我的刀打开一罐金枪鱼”这个问题。【参考方案5】:

您的代码的问题是getDeclaredMethod 函数通过名称和参数类型查找函数。随叫随到

Method retrieveItems = clazz.getDeclaredMethod("retrieveItems");

代码将查找不带参数的方法retrieveItems()。你正在寻找的方法确实需要一个参数,一个字符串,所以你应该调用

Method retrieveItems = clazz.getDeclaredMethod("retrieveItems", String.class);

这将告诉 Java 搜索 retrieveItems(String),这就是您要查找的内容。

【讨论】:

+1 - 这是异常标记的问题。检索到该方法后,必须按照@jk 的回答对其进行访问。

以上是关于使用 Java 反射访问测试用例中的受保护方法的主要内容,如果未能解决你的问题,请参考以下文章

Python - 访问类的受保护成员_

Java 反射 - 访问受保护的字段

Java - 为啥另一个包中的子级无法通过父级引用访问父级的受保护方法?

PHPUnit测试使用pdo的受保护静态方法

PHP单元测试,带模拟的受保护方法

Java中的间接子类无法访问的超类中的受保护成员