我可以模拟超类方法调用吗?

Posted

技术标签:

【中文标题】我可以模拟超类方法调用吗?【英文标题】:Can I mock a super class method call? 【发布时间】:2010-10-11 23:41:39 【问题描述】:

有时,你想测试一个类方法,并且你想对超类方法的调用做一个期望。我没有找到使用easymock或jmock在java中实现这种期望的方法(我认为这是不可能的)。

有一个(相对)干净的解决方案,使用超类方法逻辑创建一个委托,然后对其设置期望,但我不知道为什么以及何时使用该解决方案¿任何想法/示例?

谢谢

【问题讨论】:

我想不出一个真正令人信服的理由来说明这是一个坏主意。 +1 精彩的问题。 【参考方案1】:

好吧,如果你愿意,你可以。不知道大家是否熟悉JMockit,去看看吧。当前版本是0.999.17,同时,我们来看看吧……

假设以下类层次结构:

public class Bar 
    public void bar() 
        System.out.println("Bar#bar()");
    


public class Foo extends Bar 
    public void bar() 
        super.bar();
        System.out.println("Foo#bar()");
    

然后,在您的 FooTest.java 中使用 JMockit,您可以验证您实际上是在从 Foo 调用 Bar

@MockClass(realClass = Bar.class)
public static class MockBar 
    private boolean barCalled = false;

    @Mock
    public void bar() 
        this.barCalled = true;
        System.out.println("mocked bar");
    


@Test
public void barShouldCallSuperBar() 
    MockBar mockBar = new MockBar();
    Mockit.setUpMock(Bar.class, mockBar);

    Foo foo = new Foo();
    foo.bar();

    Assert.assertTrue(mockBar.barCalled);

    Mockit.tearDownMocks();

【讨论】:

也可以使用 JMockit Expectations API 来实现(请参阅code.google.com/p/jmockit 的新项目站点)。【参考方案2】:

使用 JMockit 1.22 扩展 @Cem Catikkas 答案:

@Test
public void barShouldCallSuperBar() 
    new MockUp<Bar>() 
        @Mock
        public void bar() 
            barCalled = true;
            System.out.println("mocked bar");
        
    ;

    Foo foo = new Foo();
    foo.bar();

    Assert.assertTrue(mockBar.barCalled);

不需要@MockClass注解的静态类,用MockUp类代替。

【讨论】:

【参考方案3】:

我不认为我会模拟一个超级调用 - 我觉得这种行为是类本身行为的一部分,而不是依赖项的行为。 Mocking 总是感觉它应该与依赖关系比其他任何事情都更重要。

你有一个很好的例子来说明你想模拟的那种调用吗?如果您想模拟这样的调用,是否值得考虑组合而不是继承?

【讨论】:

是的,但是你想测试隔离的子类,你知道超类工作正常,你不想再测试它。 请不要这样做。您正在打破封装以将实现锁定到测试中。除非此代码不再更改,否则您会后悔的。【参考方案4】:

在 Animated Transitions 示例测试套件中,有几个使用 JMockit 期望 API 的测试可以做到这一点(即指定超类方法的预期调用)。例如,FadeInTest 测试用例。

【讨论】:

此测试同时移至:code.google.com/p/jmockit/source/browse/trunk/samples/… 再次搬家:github.com/jmockit/jmockit1/blob/master/samples/…【参考方案5】:

不,没有办法用jMock 模拟超类方法。

但是,对于您的问题,有一个快速而简单的解决方案。假设您有 A 类和 B 类扩展 A。您想在 B 上模拟方法 Aa()。您可以在测试代码中引入 C 类扩展 B 并覆盖方法 Ca()(只需调用 super,或返回 null、id没关系)。在那个模拟 C 之后,在你可以使用 B 的任何地方使用这个模拟。

【讨论】:

没有那么脏。我们可以在测试类中创建 C 类作为内部类。 如果你调用同名的超级方法,这实际上是行不通的。 B.a() 调用 A.a()。如果你引入 C.a() 并调用 super,你最终会得到 C.a() -> B.a() -> A.a()【参考方案6】:

拦截超级调用过于细粒度。不要过度隔离。

【讨论】:

以上是关于我可以模拟超类方法调用吗?的主要内容,如果未能解决你的问题,请参考以下文章

子类可以使用超类Ios的委托方法吗

超类中的私有final可以被覆盖吗?

调用被覆盖的方法,超类调用被覆盖的方法

使用“this”调用超类方法的子类

从超类调用子类的方法

在 Python 的超类中调用超类方法