Mock在Mockito Java中继承了方法

时间:2016-07-05 09:06:56

标签: java unit-testing mockito

我的班级结构如下:

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()? (我正在编写测试的方法)

3 个答案:

答案 0 :(得分:3)

我遇到了类似的问题,所以我发现使用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();
    //...
  }
}

这种方法对我有用。

答案 1 :(得分:2)

通过这种类结构,模拟和测试真的很难。如果可能的话,我建议改变结构,因为在雾情况下,难以模拟和测试的类结构同样难以扩展和维护。

因此,如果您可以将类结构更改为类似于:

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的模拟轻松创建你的实例并且瞧.... [/ p>

如果您无法更改类结构,则需要使用Mockito.spy而不是Mockito.mock来存根特定的方法调用,但使用真实对象。

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存根该方法调用。我不知道是否有办法将超级电话存根。如果可能(如您没有覆盖类中的父方法),只需省略关键字。

答案 2 :(得分:0)

我发现了另一种方法,结果证明对我的情况非常有用。

就我而言,我需要创建一个扩展另一个类的新类,其中包括一个非常复杂的(旧版代码)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
    }
}

乍看之下,似乎只是添加一些样板代码只是为了使代码可测试。但是,我也可以将其视为代码实际外观的指示器。也许有一天(有人会发现勇气和预算;)可以重构代码,例如使用ComplexStuffExecutordoComplexStuff中的逻辑来实现Parent,将其注入MyNewClass中并摆脱继承。