模拟受保护的方法

16

我想模拟一个继承的受保护方法。由于该方法是从另一个包中的类继承而来,因此无法直接从Java代码中调用此方法。我找不到一种方法来指定在when(...)中进行存根的方法。

package a;

public class A() {
    protected int m() {}
}

package b;

public class B extends a.A {
    // this class currently does not override m method from a.A
    public asd() {}
}

// test
package b;

class BTest {
    @Test
    public void testClass() {
        B instance = PowerMockito.spy(new B());
        PowerMockito.when(instance, <specify a method m>).thenReturn(123);
        //PowerMockito.when(instance.m()).thenReturn(123); -- obviously does not work
    }
}

我查看了PowerMockito.when的重载方法,似乎它们都只能用于私有方法!

如何指定保护方法?


@AndroidKiller,更新了带有类名的代码。实际上它们可能来自Mockito,但我使用PowerMock,这些方法在那里具有相同的含义。 - michael nesterenko
这就是为什么我们应该始终优先选择组合而不是继承的原因。如果您无法像处理旧代码一样在已测试的代码中重新定义此方法,则不能这样做。 - bric3
1个回答

26

简而言之:不能总是使用when来创建测试替身;应该使用doReturn

假设已经静态导入了spydoReturn(两者都属于PowerMockito):

@RunWith(PowerMockRunner.class)
@PrepareForTest(B.class)
public class BTest {
    @Test public void testClass() throws Exception {
        B b = spy(new B());
        doReturn(42).when(b, "m");
        b.asd();
    }
}

你还可以使用@PrepareForTest(A.class),并在when(a, "m")上设置doReturn。哪种方法更合适取决于实际的测试情况。


哇,谢谢。有趣的是,当我尝试使用方法实例而不是方法字符串名称时,存根化失败并出现关于参数数量错误的异常消息。但是使用字符串名称则完美地运行。 - michael nesterenko
1
@mishanesterenko 是的,有时可能会变得有点复杂。另一个选择始终是为测试目的子类化 B。这并不总是可行的选择,但比修改字节码要少一些“神奇”;) - Dave Newton
如果你设置了一个返回void的方法,你会怎么做?假设m是一个类似于void m(MySpecialParser parser)的设置方法。这个解析器用来解析我的文本。我该如何进行模拟? - Kayser
@Kayser,难道没有一个doNothing函数吗?我现在想不起来了。 - Dave Newton
好的答案。顺便说一下:它基本上遵循了PowerMock文档中的约定(“部分模拟私有方法的完整示例”部分也适用于受保护的方法)。 - cellepo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接