mocking超类方法调用不起作用

epfja78i  于 2021-07-06  发布在  Java
关注(0)|答案(4)|浏览(499)

我一直在尝试从子类重写的方法中截取超类方法调用,但是直到现在我仍然没有成功的希望。我在google上搜索了很多问题。
下面是我正在使用的测试代码。问题是,在我的例子中,超类和子类方法都被存根了。

@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"));
}

属性设置在 ResultObjectSubclass's getObject(...) method . 我想把这根树枝剪短 super.getObject(...) call within the subclass's to return some arbitrary object 由提供 getReturnObject() method .
出现的问题是:即使是电话 ResultObject result= adc.getObject("acc", "", null); 正在被存根,而属性未被设置,这导致了问题。
我甚至试着加上: doCallRealMethod().when(adc).getObject(any(String.class), any(String.class), any(Map.class)); 就在示例上的实际调用之前,希望示例上的实际方法被调用。但在本例中,super.getobject(…)不会被存根和执行。
这是一种非此即彼的情况,我陷入其中,我既可以存根两者或不能存根任何。请帮帮我!

eit6fx6z

eit6fx6z1#

您应该能够更改尝试强制转换的代码:
adc.super.getobject();
正如我在上面的评论中所说的,根据docs,java中的super关键字是一个引用变量,它提供了对对象父对象的引用。如果演员阵容确实有问题,这个改变应该可以解决。

ffscu2ro

ffscu2ro2#

这是一个有趣的问题。当您使用继承时,即使您在testcase中模拟父类,在子类的自动连接过程中,它总是引用实际的父类,而不是模拟的父类。这与mock框架有关。似乎它缺少了允许在子示例化期间模拟父类的功能
如果您使用组合而不是继承,您将获得结果。但是我怀疑是否有人会想要改变一个好的设计来执行一个测试用例:d

@Component("parent")
public class Parent{   
    public String getMsg(){
        return "Parent";
    }
}
@Component
@Lazy
public class Child {
    @Autowired
    Parent parent; 
    public String getMsg(){
        return "Child" + parent.getMsg();
    }
}

The test case - 
 @MockBean
    @Qualifier("parent")
    Parent base;

 @BeforeEach
    public void initMockedBean(){
 when(base.getMsg()).thenReturn("Dummy"); }

如果有可能只测试父代而不是子代,你可以试试
你可以试着在mockito上提出这个问题。可能他们会把它添加到功能中
在回答这个问题的时候,我发现了一个线索来证实我的答案——mockito如何只模拟超类方法的调用——这里还有一个建议。你可以试试看这是否对你有帮助。

nfeuvbwi

nfeuvbwi3#

有几个选择
重构以支持组合而不是继承(有很多关于好处的文章,这是首选策略)
重命名方法,使它们在 Parent 以及 Child 将呼叫移动到 super 变成一个 private 上的方法 Child
改变 Parent 使用字节操作
一些示例使用powermock和bytebuddy

示例-重构

public class A {
    public String getName() {
        return "BOB";
    }
}

public class B {

    private final A a;

    public B(A a) {
        this.a = a;
    }

    public String getName() {
        String name = a.getName();
        return name;
    }
}

class BTest {

    @Test
    void shouldMockA() {
        A a = mock(A.class);
        when(a.getName()).thenReturn("JANE");
        assertThat(a.getName(), is("JANE"));
    }

}

示例-powermock重命名

这个例子使用的是junit,请按照下面的链接来设置testng

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"));
    }

}

示例-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等)

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
    }

}
pw9qyyiw

pw9qyyiw4#

此施法不会产生您正在尝试的效果:

((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

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

相关问题