自动委派实施方法

时间:2019-03-26 15:53:46

标签: java unit-testing proxy mockito delegation

我可以自动将所有调用委派给共享同一接口的另一个实例的方法吗?

我有一个大班级(我想测试),像这样:

class MyClassUnderTest implements SomeInterface {
  public void anImportantMethod() {
  }
  @Override
  public void fromTheInterface() {
  }
  @Override
  public void fromTheInterface2() {
  }
  private void utilFunc() {}
}

它实现了一个接口

interface SomeInterface {
  void fromTheInterface();
  void fromTheInterface2();
}

鉴于在单元测试期间,我想“隐藏”从接口继承的具有不同实现的方法。为此,我编写了一个实用程序类

class DebugSomeInterface implements SomeInterface {
  @Override public void fromTheInterface() { log.debug("1"); }
  @Override public void fromTheInterface2() { log.debug("2"); };
}

现在,我需要“委托”该实现的所有可能调用。我是通过MyClassUnderTest我自己的课程派生的,将所有的调用委派给我来做的:

class MyClassUnderTest_Mock extends MyClassUnderTest {
    DebugSomeInterface delegated = new DebugSomeInterface();

  @Override public void fromTheInterface() {
    delegated.fromTheInterface();
  }
  @Override public void fromTheInterface2() { 
    delegated.fromTheInterface2();
  };

仅,我有很多实现SomeInterface的类,并且手工完成很繁琐且容易出错。

我想要一种(半)自动的方式来创建MyClassUnderTest_Mock实例,就像mock(...)用Mockito那样。也许像

 MyClassUnderTest underTest = new MyClassUnderTest();
 DebugSomeInterface delegated = new DebugSomeInterface();
 MyClassUnderTest instance = mixin(underTest, SomeInterface.class, delegated);

这可能会创建一个“代理” instance,该代理将从SomeInterfacedelegated的所有方法调用都委派给其他人,而其余的则委托给underTest

在核心Java中,代理对象有某种机制,但是我无法将它们全部结合在一起。

1 个答案:

答案 0 :(得分:0)

您可以执行以下操作:

class ImageCheck(Ui_ImageCheck.Ui_MainWindow, QMainWindow):
    def __init__(self, parent=None):
        super(ImageCheck, self).__init__()
        self.setupUi(self)
        self.setWindowTitle("Image Analyzer")

        self.crop_ratio_w = 1
        self.crop_ratio_h = 1

        self.path = None
        self.scene = QGraphicsScene()
        self.scene.clear()
        self.image_item = QGraphicsPixmapItem()
        # This is the approximate shift in coordinates of my initial view from the window
        self.view.setGeometry(self.geometry().x()+ 10, self.geometry().y()+ 39, 
                    self.geometry().width()- 55, self.geometry().height()- 110)

        self.view.setAlignment(Qt.AlignCenter)
        self.view.setFrameShape(QFrame.NoFrame)

        def setImage(self, path):
           self.path = path
           self.crop_ratio_w = self.pixmap.width() / self.view.width()
           self.crop_ratio_h = self.pixmap.height() / self.view.height()

           pixmap = QPixmap(path)
           smaller_pixmap = pixmap.scaled(self.view.width(), self.view.height(),
                    Qt.IgnoreAspectRatio, t.FastTransformation)

           self.image_item.setPixmap(smaller_pixmap)
           self.scene.addItem(self.image_item)

           self.scene.setSceneRect(0, 0, self.view.width(), self.view.height())
           self.view.setGeometry(0, 0, self.view.width(), self.view.height())   

           self.view.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
           self.view.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
           self.view.setScene(self.scene)
           self.view.setSceneSize()

        def resizeEvent(self, event):
           self.view.setGeometry(self.geometry().x()+ 10, self.geometry().y()+ 39, 
                    self.geometry().width()- 55, self.geometry().height()- 110)
           self.setImage(self.path)

然后像这样使用它:

public static <T, U extends T> U createProxy(U classUnderTest, Class<T> interfaceType, T debugImplementation)
        throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
    U spy = Mockito.spy(classUnderTest);
    for (Method m : interfaceType.getMethods()) {
        Object[] params = new Object[m.getParameterTypes().length];
        for (int i = 0; i < params.length; i++) {
            params[i] = Mockito.any();
        }
        Mockito.when(m.invoke(spy, params)).thenAnswer(new Delegate(debugImplementation, m));
    }
    return spy;
}

public static class Delegate implements Answer {
    private final Object delegate;

    private final Method delegateMethod;

    public Delegate(Object delegate, Method delegateMethod) {
        this.delegate = delegate;
        this.delegateMethod = delegateMethod;
    }

    @Override
    public Object answer(InvocationOnMock invocation) throws Throwable {
        return delegateMethod.invoke(delegate, invocation.getArguments());
    }
}

这将按您希望的方式工作,但是作为免责声明,我认为这是个坏主意...不得不监视被测对象并模拟出被测对象的位应该是一个危险的标志...这是一种不好的做法,只有在代码复杂度高于最佳代码并且类的各个部分应该拆分为其他类时,才需要执行此类操作。我从未遇到过这样的情况,在这种情况下,重构逻辑并不是更好的解决方案...