簡體   English   中英

使用 mockito 的 Mocking 超類方法調用不起作用

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

我一直在嘗試從子類覆蓋的方法中對超類方法調用存根,但直到現在我都沒有任何成功的運氣。 我也廣泛搜索了谷歌和 SO 問題。

這是我正在使用的測試代碼。 問題是,就我而言,超類和子類方法都被存根了。

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

該屬性在Subclass's getObject(...) method中的ResultObject上設置。 我想存根super.getObject(...) call within the subclass's to return some arbitrary objectgetReturnObject() method提供的一些任意 object 。

正在發生的問題是:即使調用ResultObject result= adc.getObject("acc", "abc", null); 正在被存根並且屬性未設置,這導致了問題。

我什至嘗試添加: doCallRealMethod().when(adc).getObject(any(String.class), any(String.class), any(Map.class)); 就在實例上的實際調用之前,希望實例上的實際方法被調用。 但在這種情況下, super.getObject(...) 不會被存根並被執行。

這是我陷入困境的一種非此即彼的情況,我可以同時存根或不能存根。 請幫忙!

此演員表不會產生您正在嘗試的效果:

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

調用的 getObject 方法是 adc 實例之一,與強制轉換無關。 演員表只是編譯時的糖。

您需要更改設計,例如使用不同的方法名稱或組合來代替 inheritance。 其他可能性是在測試運行時覆蓋超類,將修改后的版本復制到具有與原始 package 相同的測試源:

像這樣:

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

這只會影響您的測試。 測試包在運行時不可用。

有幾個選項

  • 重構以支持 inheritance 的組合(有很多關於好處的文章,將是首選策略)
  • 重命名方法,使它們在ParentChild中不同
  • 將對super的調用移動到Child class 上的private方法中
  • 使用字節操作更改Parent

一些示例使用PowerMockByteBuddy

示例 - 重構

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
    }

}

您應該能夠更改嘗試強制轉換的代碼:

adc.super.getObject();

正如我在上面的評論中所說,根據文檔,Java 中的 super 關鍵字是一個引用變量,它提供對對象父級的引用。 如果演員陣容確實是問題所在,則此更改應該可以解決問題。

這是個有趣的問題。 當您使用 inheritance 時,即使您在測試用例中模擬父 class,在子的自動裝配期間,它始終指的是實際的父 class,而不是模擬的那個。 這與 Mock 框架有關。 似乎它缺少允許在子實例化期間模擬父類的能力

  1. 如果您使用組合而不是 inheritance 您將獲得結果。 但我懷疑有人會想要改變一個好的設計來執行一個測試用例: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. 如果可以只測試父母而不是孩子,你可以試試

您可以嘗試在 Mockito 上提出問題。 也許他們會將其添加到功能中

在回答這個問題時,我發現這個線程證實了我的回答 - Mockito How to mock only the call of a method of the superclass還有一個建議。 你可以試試看是否對你有幫助。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM