【问题标题】:Is there any way to replace dynamic methods?有什么办法可以代替动态方法吗?
【发布时间】:2011-03-29 16:34:10
【问题描述】:

假设我们有一个接口,它有两个方法:

public interface MyInterface {
    public SomeType first();
    public SomeType second();
}

该接口由MyInterfaceImpl实现。在实现内部,first() 调用 second() 来检索一些结果。

我想构建一个单元测试,它会根据来自second() 的内容断言来自first() 的内容,类似于:

1  public class MyInterfaceTest {
2     private MyInterface impl = new MyInterfaceImpl();

4     @Test
5     public void testFirst() {
6         // modify behaviour of .second()
7         impl.first();
8         assertSomething(...);

10        // modify behaviour of .second()
11        impl.first();
12        assertSomethingElse(...);
13    }
14 }

有没有一种简单的方法可以在2 行上创建一个模拟,以便直接调用对选定方法(例如first())的所有调用(委托给MyInterfaceImpl)而其他一些方法(例如@987654331 @) 替换为模拟对应项?

对于静态方法,这实际上很容易通过 PowerMock 实现,但对于动态方法,我需要类似的东西。

解决方案基于

MyInterface mock = EasyMock.createMock(MyInterface.class);
MyInterface real = new MyInterfaceImpl();
EasyMock.expect(mock.first()).andReturn(real.first()).anyTimes();
EasyMock.expect(mock.second()).andReturn(_somethingCustom_).anyTimes();

还不够好,尤其是对于具有大量方法(大量样板)的接口。我需要转发行为,因为real 实际上取决于其他模拟。

我希望这样的事情由框架来处理,而不是由我自己的类来处理。这可以实现吗?

【问题讨论】:

    标签: java unit-testing junit easymock powermock


    【解决方案1】:

    如果first() 的实现必须调用second(),听起来您很可能应该为first()second() 提供单独的接口。然后,您也可以拆分实现,并在测试 first() 时模拟 second()。如果没有更具体的例子来说明 first()second() 是什么,很难确定。

    在实现类上使用 EasyMock 来模拟 second() 调用 可能 工作,但您似乎不想这样做。这可能需要告诉 EasyMock 将对 first() 的调用传递给正常的实现 - 我不确定。

    另一种选择可能是将测试类中的实现子类化(作为嵌套类),允许您仅覆盖 second() 以进行测试。还是挺丑的。

    就我个人而言,我不喜欢为了测试其余部分而伪造课程的部分。我宁愿伪造所有类的依赖项。

    【讨论】:

    • 不幸的是,拆分 first() 和 second() 不是一种选择——我们的设计以业务逻辑类型为中心,其想法是保留所有适用于该类型工作的函数同一个屋顶。不过感谢您的回答。
    • @mindas:我鼓励你至少重新审视这个决定。例如,帐户身份验证器不应与帐户报告生成器属于同一类型。如果您受到丑陋的设计决策的束缚,那么当测试(和实现)也变得丑陋时,您不应该感到惊讶:((当然,这是假设它真的一个丑陋的设计。我'我在猜测,但听起来像......)
    • 这有点离题了,但你真的是在建议方法不应该使用同一类的其他公共方法吗?例如。在 Java 中 String.equalsIgnoreCase 调用 String.regionMatches - 尽管这些是不同的函数,但它们并没有不同,可以单独放置。也许这是一个不好的例子,因为 String 不是接口,但我想指出我的情况非常相似,所以 first() 和 second() 做的事情非常接近。
    【解决方案2】:

    好的旧子类化怎么样?我的意思是像

    private MyInterface impl = new MyInterfaceImpl(){
        public final MyInterface mock = EasyMock.createMock(MyInterface.class);
        @override //only the method you need to mock
        public SomeType second(){
            return mock.second();        
        }
    }
    
    @Test
    public void testFirst() {
        // modify behaviour of .second()
        EasyMock.expect(impl.mock.second()).andReturn("What I want").anyTimes();
        impl.first();
        assertSomething(...);
    
        // modify behaviour of .second()
        EasyMock.expect(impl.mock.second()).andReturn("Now I want something else").anyTimes();
        impl.first();
        assertSomethingElse(...);
    }
    

    您不是在测试您要测试的确切类,而是一个匿名子类。但是我们可以假设子类化在 Java 中可以正常工作。;-)

    【讨论】:

    • 我实际上已经按照这些思路实现了一些东西。会接受这个答案,因为我不认为 EasyMock 可以做我想做的事情:(
    【解决方案3】:

    您可能可以使用Dynamic Proxy

    【讨论】:

    • 非常有趣的建议。然而它在这里不适用——当在 first() 内部时,second() 是基于 this 调用的,它不能被伪造/代理(除非 MyImpl 会动态调用 second(),这没有意义)。但是无论如何感谢您的建议,因为它我现在学到了一些关于动态代理的有趣的东西:)
    猜你喜欢
    • 2011-08-20
    • 2021-08-20
    • 2017-03-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-02-06
    • 2012-01-24
    • 1970-01-01
    相关资源
    最近更新 更多