Java 使用mockito模拟超类方法调用不起作用

Java 使用mockito模拟超类方法调用不起作用,java,mockito,testng,stub,Java,Mockito,Testng,Stub,我一直试图从子类重写的方法中存根超类方法调用,但到目前为止,我一直没有成功的希望。我在谷歌上搜索了很多问题 下面是我正在使用的测试代码。问题是,在我的例子中,超类和子类方法都被打断了 @Test(enabled = true) public void superclassMockTest() throws Exception { ChildClass adc = getChildClass (); doReturn(getReturnObject())

我一直试图从子类重写的方法中存根超类方法调用,但到目前为止,我一直没有成功的希望。我在谷歌上搜索了很多问题

下面是我正在使用的测试代码。问题是,在我的例子中,超类和子类方法都被打断了

@Test(enabled = true)
public void superclassMockTest() throws Exception {

    ChildClass adc = getChildClass ();

    doReturn(getReturnObject())
            .when((SuperClass) adc).getObject(any(String.class), any(String.class), any(Map.class))
    
    ResultObject result= adc.getObject("acc", "abc", null);
    assertNotNull(result);
    assertNotNull(result.getPropertyValue("attribute"));
}
属性是在
子类的getObject(…)方法中的
ResultObject
上设置的。我想在子类中存根
super.getObject(…)调用,以返回由
getReturnObject()方法提供的一些任意对象

出现的问题是:甚至调用
ResultObject result=adc.getObject(“acc”,“abc”,null)被存根,而属性未被设置,这导致了问题

我甚至尝试添加:
doCallRealMethod().when(adc).getObject(any(String.class)、any(String.class)、any(Map.class))就在实例上的实际调用之前,希望实例上的实际方法被调用。但是在本例中,super.getObject(…)没有被存根和执行


这是一种非此即彼的情况,我陷入其中,我要么两者都可以存根,要么任何一个都不能存根。请帮忙

这是一个有趣的问题。 当您使用继承时,即使您在测试用例中模拟父类,在子类的自动连接过程中,它始终引用实际的父类,而不是模拟的父类。 这与模拟框架有关。似乎它缺少在子类实例化期间模拟父类的功能

  • 如果您使用组合而不是继承,您将获得结果。但我怀疑是否有人会想要改变一个好的设计来执行一个测试用例:D
  • 如果可以只测试父对象而不是子对象,您可以尝试一下
  • 你可以试着在Mockito上提出这个问题。可能他们会将其添加到功能中

    在回答这个问题时,我发现这条线索证实了我的答案-
    这里还有一个建议。你可以试着看看这是否对你有帮助。

    这个角色不会产生你正在尝试的效果:

    ((SuperClass) adc).getObject("", "", null)
    
    调用的getObject方法是adc实例的方法之一,与强制转换无关。演员阵容仅在编译时使用

    您需要更改设计,比如使用不同的方法名,或者使用组合而不是继承。另一种可能性是在测试运行时重写超类,使用与原始版本相同的包将修改后的版本复制到测试源:

    像这样:

    src/main/java
                 \-------- com.xyz.pac
                      \--------- SuperClass.java
                      \--------- ChildClass.java
    src/test/java
                 \-------- com.xyz.pac
                      \--------- SuperClass.java
                 \-------- test.com.xyz
                      \--------- MyTest.java
    

    这只会影响您的测试。测试包在运行时不可用。

    有几个选项

    • 重构以支持组合而不是继承(有很多关于好处的文章,这将是首选策略)
    • 重命名方法,使它们在
      父级
      子级
    • 将对
      super
      的调用移动到
      Child
      类上的
      private
      方法中
    • 使用字节操作更改父级
    一些示例使用和

    示例-重构 示例-PowerMock重命名 **此示例使用JUnit,请按照此设置TestNG**

    public class Child extends Parent {
    
        public String getName() {
            String name = callParentGetName();
            return name;
        }
    
        private String callParentGetName() {
            return super.getName();
        }
    }
    
    @RunWith(PowerMockRunner.class)
    @PrepareForTest({Child.class, Parent.class})
    public class ChildTest {
    
        private final Child child = PowerMockito.spy(new Child());;
    
        @Test
        public void shouldMockParentSuperCallName() throws Exception {
            PowerMockito.doReturn("JANE").when(child, "callParentGetName");
            assertThat(child.getName(), is("JANE"));
        }
    
    }
    
    示例-PowerMock私有方法 **此示例使用JUnit。请按照此链接设置TestNG**

    public class Child extends Parent {
    
        public String getName() {
            String name = callParentGetName();
            return name;
        }
    
        private String callParentGetName() {
            return super.getName();
        }
    }
    
    @RunWith(PowerMockRunner.class)
    @PrepareForTest({Child.class, Parent.class})
    public class ChildTest {
    
        private final Child child = PowerMockito.spy(new Child());;
    
        @Test
        public void shouldMockParentSuperCallName() throws Exception {
            PowerMockito.doReturn("JANE").when(child, "callParentGetName");
            assertThat(child.getName(), is("JANE"));
        }
    
    }
    
    示例-ByteBuddy **不建议这样做(查看Java代理、Instrumentation ByteBuddy等)**


    您应该能够更改尝试强制转换的代码:

    adc.super.getObject()


    正如我在上面的评论中所说,根据文档,Java中的super关键字是一个引用变量,它提供了对对象父对象的引用。如果强制转换确实是问题所在,那么此更改应该可以解决它。

    除非您提供正在测试的代码,否则很难真正理解您的问题。正如我所解释的那样。考虑一个典型的场景。有一个特定的超类,一个子类,然后我想从超类中存根XYZ()方法,只想从子类中执行相同的XYZ()方法。实际逻辑在超类的方法中,该方法返回一个ResultObject,而ChildClass的方法只是在ResultObject上设置了更多的东西!我想从ChildClass中存根super.XYZ()调用,该调用将返回一些任意构建的ResultObject。这就是doReturn(…)。当((超类)…)应该做的事情时,问题是:doReturn(getReturnObject())。当((超类)adc)。getObject(任意(String.class)、任意(String.class)、任意(Map.class));这一行甚至中断了对ChildClass的XYZ()方法的调用。因此,对ChildClass的XYZ()方法的调用将返回任意ResultObject,这实际上应该发生在它在自身内部调用super.XYZ()时。因此,不会执行ChildClass的XYZ()方法中的额外逻辑,也不会在ResultObject上设置额外属性!测试用例验证失败。有关代码的更多信息,两个方法(重写和重写)都是公共的,没有什么更奇怪或特别的。直截了当的情景。想知道为什么会发生这种情况吗?是什么原因导致ChildClass的方法被我在问题中发布的测试代码打断。因此,将代码添加到您的问题中,为什么有人需要阅读您对该问题的注释才能理解它?您能否提供一个示例:其他可能性是在测试运行时重写超类,复制一个修改过的版本来测试源代码,并使用相同的原始软件包。@neerajdorle我刚刚编辑了我的答案,如果他调用adc.Super.getObject(…)?那会调用父方法吗?Super ref变量向pa返回一个ref
    public class Parent {
        
        public String getName() {
            return "BOB";
        }
        
    }
    
    public class Child extends Parent {
    
        public String getNameChild() {
            String name = super.getName();
            return name;
        }
    }
    
    @RunWith(PowerMockRunner.class)
    @PrepareForTest(Parent.class)
    public class ChildTest {
    
        private final Child child = PowerMockito.spy(new Child());
    
        @Test
        public void shouldMockParentSuperCall() {
            PowerMockito.doReturn("JANE").when(child).getName();
            assertThat(child.getNameChild(), is("JANE"));
        }
    
    }
    
    public class Child extends Parent {
    
        public String getName() {
            String name = callParentGetName();
            return name;
        }
    
        private String callParentGetName() {
            return super.getName();
        }
    }
    
    @RunWith(PowerMockRunner.class)
    @PrepareForTest({Child.class, Parent.class})
    public class ChildTest {
    
        private final Child child = PowerMockito.spy(new Child());;
    
        @Test
        public void shouldMockParentSuperCallName() throws Exception {
            PowerMockito.doReturn("JANE").when(child, "callParentGetName");
            assertThat(child.getName(), is("JANE"));
        }
    
    }
    
    public class Parent {
    
        public String getName() {
            return "BOB";
        }
    
    }
    
    public class Child extends Parent {
    
        public String getName() {
            String name = super.getName();
            return name;
        }
    
    }
    
    class ChildTest {
    
        @Test
        void shouldChangeParentMethod() {
            ByteBuddyAgent.install();
            new ByteBuddy()
                    .redefine(Parent.class)
                    .method(named("getName"))
                    .intercept(FixedValue.value("JANE"))
                    .make()
                    .load(
                            Parent.class.getClassLoader(),
                            ClassReloadingStrategy.fromInstalledAgent());
    
            Child child = new Child();
    
            assertThat(child.getName(), is("JANE"));
    
            // Remove anything added e.g. class transformers
        }
    
    }