简体   繁体   English

使用 mockito 的 Mocking 超类方法调用不起作用

[英]Mocking superclass method call using mockito is not working

I have been trying to stub the superclass method call from the subclass overridden method, but till now I am stuck without any luck to succeed.我一直在尝试从子类覆盖的方法中对超类方法调用存根,但直到现在我都没有任何成功的运气。 I have extensively searched on the google and SO questions as well.我也广泛搜索了谷歌和 SO 问题。

Here is the test code that I am using.这是我正在使用的测试代码。 The problem is that, in my case, both the superclass and subclass methods are getting stubbed.问题是,就我而言,超类和子类方法都被存根了。

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

The property is set on the ResultObject in the Subclass's getObject(...) method .该属性在Subclass's getObject(...) method中的ResultObject上设置。 I want to stub the super.getObject(...) call within the subclass's to return some arbitrary object which is provided by getReturnObject() method .我想存根super.getObject(...) call within the subclass's to return some arbitrary objectgetReturnObject() method提供的一些任意 object 。

The problem that is occurring is that: even the call ResultObject result= adc.getObject("acc", "abc", null);正在发生的问题是:即使调用ResultObject result= adc.getObject("acc", "abc", null); is getting stubbed and the property is not getting set, which is causing the problem.正在被存根并且属性未设置,这导致了问题。

I even tried adding: doCallRealMethod().when(adc).getObject(any(String.class), any(String.class), any(Map.class));我什至尝试添加: doCallRealMethod().when(adc).getObject(any(String.class), any(String.class), any(Map.class)); just before the actual call on the instance, hoping that the actual method on the instance is called.就在实例上的实际调用之前,希望实例上的实际方法被调用。 But in this case, the super.getObject(...) is not getting stubbed and getting executed.但在这种情况下, super.getObject(...) 不会被存根并被执行。

It's kind of either or situation into which I am stuck, I can either stub both or can't stub any.这是我陷入困境的一种非此即彼的情况,我可以同时存根或不能存根。 Please help!请帮忙!

This cast will not take the effect you are trying:此演员表不会产生您正在尝试的效果:

((SuperClass) adc).getObject("", "", null)

The getObject method that is called is the one of the adc instance, regardless of the cast.调用的 getObject 方法是 adc 实例之一,与强制转换无关。 The cast is compile-time sugar only.演员表只是编译时的糖。

You'll need to change the design, like using different method names, or composition instead of inheritance.您需要更改设计,例如使用不同的方法名称或组合来代替 inheritance。 Other possibility is to override the superclass in test runtime, copying a modified version to test source with same package of the original:其他可能性是在测试运行时覆盖超类,将修改后的版本复制到具有与原始 package 相同的测试源:

Like this:像这样:

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

This only affect your tests.这只会影响您的测试。 Test packages are not available at runtime.测试包在运行时不可用。

Theer are a few options有几个选项

  • Refactor to favor composition over inheritance (there are lots of articles on the benefits and would be the preferred strategy)重构以支持 inheritance 的组合(有很多关于好处的文章,将是首选策略)
  • Rename the the methods so they are different in Parent and Child重命名方法,使它们在ParentChild中不同
  • Move the call to super into a private method on the Child class将对super的调用移动到Child class 上的private方法中
  • Change Parent using byte manipulation使用字节操作更改Parent

Some examples use PowerMock and ByteBuddy一些示例使用PowerMockByteBuddy

Example - Refactor示例 - 重构

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

}

Example - PowerMock Rename示例 - PowerMock 重命名

** This example is using JUnit please follow this link to setup for TestNG ** **此示例使用 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"));
    }

}

Example - PowerMock Private Method示例 - PowerMock 私有方法

** This example is using JUnit please follow this link to setup for TestNG ** ** 此示例使用 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"));
    }

}

Example - ByteBuddy示例 - ByteBuddy

** This is not recommended (look into Java Agents, Instrumentation ByteBuddy, etc) ** ** 不建议这样做(查看 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
    }

}

You should be able to change out the code which attempts the cast for:您应该能够更改尝试强制转换的代码:

adc.super.getObject(); adc.super.getObject();

As I stated in my comment above, the super keyword in Java is, according to the docs, a reference variable which gives a reference to the object's parent.正如我在上面的评论中所说,根据文档,Java 中的 super 关键字是一个引用变量,它提供对对象父级的引用。 If the cast is indeed the problem, this change should fix it.如果演员阵容确实是问题所在,则此更改应该可以解决问题。

This is an interesting question.这是个有趣的问题。 When you use inheritance, even though you mock the parent class in your testcase, during autowiring of the child it always refers to the actual parent class, not the one mocked.当您使用 inheritance 时,即使您在测试用例中模拟父 class,在子的自动装配期间,它始终指的是实际的父 class,而不是模拟的那个。 This is something to do with Mock framework.这与 Mock 框架有关。 Seems it is missing the capability of allowing Parent classes to be mocked during child's instantiation似乎它缺少允许在子实例化期间模拟父类的能力

  1. if you use composition instead of inheritance you will achieve the results.如果您使用组合而不是 inheritance 您将获得结果。 But I doubt anyone would want to change a good design to be able to execute a test case:D但我怀疑有人会想要改变一个好的设计来执行一个测试用例: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"); }

  1. if it's possible to just test the Parent instead of the child, you can just try that如果可以只测试父母而不是孩子,你可以试试

You can try raising the issue on Mockito.您可以尝试在 Mockito 上提出问题。 May be they will add it to features也许他们会将其添加到功能中

While answering this question, I found this thread that confirms my answer - Mockito How to mock only the call of a method of the superclass There's also one suggestion in there.在回答这个问题时,我发现这个线程证实了我的回答 - Mockito How to mock only the call of a method of the superclass还有一个建议。 You can try to see if that helps you.你可以试试看是否对你有帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM