Mockito Java中的模拟继承方法

0sgqnhkj  于 2022-11-08  发布在  Java
关注(0)|答案(4)|浏览(352)

我的类结构如下:

public class MyParentClass {

    void doSomethingParent() {
        System.out.println("something in parent");
    }
}

public class MyClass extends MyParentClass {

    protected String createDummyRequest(Holder myHolder) {
        //...
        super.doSomethingParent();//I want to avoid this
        //...
        callingDB();
        return "processedOutput";
    }

    private void callingDB() {
        System.out.println("Calling to DB");
    }
}

然后我的单元测试:

public class UnitTest {

    public void testCreateDummyRequest() {
        //create my mock holder
        Holder mockHolder = new Holder();

        MyClass mockObj = Mockito.mock(MyClass.class);
        //mock doSomethingParent()
        //mock callingDB()

        //as mockObj is a fully mock, but I need to run my real method
        //Mockito.when(mockObj.createDummyRequest(mockHolder)).thenCallRealMethod();
        mockObj.createDummyRequest(mockHolder);
        //Problem: doSomethingParent() is getting called though I have mocked it
    }
}

我如何防止在我的方法中调用super.doSomethingParent()?(我正在编写测试的方法)

but5z9lq

but5z9lq1#

这种类结构很难模拟和测试。如果可能的话,我建议改变这种结构,因为在某些情况下,一个很难模拟和测试的类结构同样很难扩展和维护。
因此,如果您可以将类结构更改为类似于:

public class MyClass {

    private DoSomethingProvider doSomethingProvider;

    private DbConnector dbConnector;

    public MyClass (DoSomethingProvider p, DbConnector c) {
        doSomethingProvicer = p;
        dbConnector = c;
    }

    protected String createDummyRequest(Holder myHolder){
        //...
        doSomethingProvider.doSomethingParent();
        //...
        dbConnector.callingDB();
        return "processedOutput";
    }
}

然后,您可以轻松地使用DoSomethingProvider和DbConnector的模拟创建示例,瞧......
如果你不能改变你的类结构,你需要使用Mockito.spy而不是Mockito.mock来stub特定的方法调用,但是使用真正的对象。

public void testCreateDummyRequest(){
    //create my mock holder
    Holder mockHolder = new Holder();

    MyClass mockObj = Mockito.spy(new MyClass());

    Mockito.doNothing().when(mockObj).doSomething();

    mockObj.createDummyRequest(mockHolder);
}

注意:使用super关键字可以防止Mockito存根该方法调用。我不知道是否有一种方法可以存根对super的调用。如果可能的话(比如你没有覆盖你类中的父方法),就省略该关键字。

xoefb8l8

xoefb8l82#

我遇到了类似的问题,所以我发现使用spy()可以帮助。

public class UnitTest {

  private MyClass myObj;

  @Before
  public void setUp() throws Exception {
    MockitoAnnotations.initMocks(this);
    myObj= spy(new MyClass());
  }

  @Test
  public void mockedSuperClassMethod(){
    doNothing().when((MyParentClass )myObj).doSomethingParent();
    //...
  }
}

这种方法对我很有效。

uemypmqf

uemypmqf3#

我找到了另一种方法,结果证明对我的情况非常有用。
在我的例子中,我需要创建一个新的类来扩展另一个类,其中包括一个非常复杂的(遗留代码)protected final方法。由于这种复杂性,重构来使用组合是不可能的,所以下面是我的想法。
假设我有以下内容:

abstract class Parent {

    public abstract void implementMe();

    protected final void doComplexStuff( /* a long parameter list */) {
        // very complex legacy logic
    }

}

class MyNewClass extends Parent {

    @Override
    public void implementMe() {
        // custom stuff
        doComplexStuff(/* a long parameter list */); // calling the parent
        // some more custom stuff
    }

}

下面是我重新排列代码的方法:

abstract class Parent {

    public abstract void implementMe();

    protected final void doComplexStuff( /* a long parameter list */) {
        // very complex legacy logic
    }

}

interface ComplexStuffExecutor {
    void executeComplexStuff(/* a long parameter list, matching the one from doComplexStuff */);
}

class MyNewClass extends Parent {

    private final ComplexStuffExecutor complexStuffExecutor;

    MyNewClass() {
        this.complexStuffExecutor = this::doComplexStuff;
    }

    MyNewClass(ComplexStuffExecutor complexStuffExecutor) {
        this.complexStuffExecutor = complexStuffExecutor;
    }

    @Override
    public void implementMe() {
        // custom stuff
        complexStuffExecutor.doComplexStuff(/* a long parameter list */); // either calling the parent or the injected ComplexStuffExecutor
        // some more custom stuff
    }

}

当为“生产”目的创建MyNewClass的示例时,我可以使用默认构造函数。
然而,在编写单元测试时,我会使用构造函数,在这里我可以注入ComplexStuffExecutor,在那里提供一个模拟,并且只测试来自MyNewClass的自定义逻辑,即:

class MyNewClassTest {

    @Test
    void testImplementMe() {
        ComplexStuffExecutor complexStuffExecutor = Mockito.mock(ComplexStuffExecutor.class);
        doNothing().when(complexStuffExecutor).executeComplexStuff(/* expected parameters */);
        MyNewClass systemUnderTest = new MyNewClass(complexStuffExecutor);
        // perform tests
    }
}

初看起来,似乎只是为了使代码可测试而添加了一些样板代码。然而,我也可以将其视为代码实际应该是什么样子的一个指标。也许有一天有人(他会找到勇气和预算;))可以重构代码,例如用来自ParentdoComplexStuff的逻辑实现ComplexStuffExecutor,将其注入到MyNewClass中,并摆脱继承。

hjqgdpho

hjqgdpho4#

以下是具体操作方法

public class BaseController {

     public void method() {
          validate(); // I don't want to run this!
     }
}
public class JDrivenController extends BaseController {
    public void method(){
        super.method()
        load(); // I only want to test this!
    }
}

@Test
public void testSave() {
    JDrivenController spy = Mockito.spy(new JDrivenController());

    // Prevent/stub logic in super.method()
    Mockito.doNothing().when((BaseController)spy).validate();

    // When
    spy.method();

    // Then
    verify(spy).load();
}

来源:https://blog.jdriven.com/2013/05/mock-superclass-method-with-mockito/

相关问题