使用 Mockito 拦截真正的非静态方法调用
Intercepting real non-static method calls with Mockito
有什么方法可以使用 Mockito
或 PowerMockito
来拦截对对象或至少是单例对象的非静态方法的调用?
以下 classes 提供了示例:
public class Singleton {
private static Singleton INSTANCE = null;
private Singleton(Object parameter) {}
public static Singleton getInstance(Object parameter) {
if (INSTANCE == null) {
INSTANCE = new Singleton(parameter);
}
return INSTANCE;
}
public String process(String a, String b) {
return (a + b);
}
// Other methods
}
public class Foreign {
private Foreign() {}
public static void main(String[] args) {
System.out.println(Singleton.getInstance(new Object()).process("alpha", "beta"));
}
}
Singleton
对象是在 Foreign
class 中创建的,不受某些测试代码(上面未显示)的控制。这两个class都不能修改。 objective 是拦截对测试代码中非静态 process()
方法的调用,以便对于某些值,不同的结果是 returned,例如通话
Singleton.getInstance(new Object()).process("alpha", "beta");
模拟为 return "alpha-beta"
而不是预期的 "alphabeta"
.
一个解决方案可能是拦截 Singleton.getInstance()
方法来实例化 Singleton 的自定义子 class,例如使用
public class SubSingleton extends Singleton {
public SubSingleton(Object parameter) {
super(parameter);
}
public String process(String a, String b) {
if ("alpha".equals(a) && "beta".equals(b)) {
return a + "-" + b;
}
return super.process(a + b);
}
}
然后,对 Singleton.process()
方法的调用将被拦截,如下所示:
Object parameter = new Object();
PowerMockito.doReturn(new SubSingleton(parameter)).when(Singleton.class, "getInstance", parameter);
但是上面的Singleton
class只提供了一个私有的构造函数,所以不能扩展。使用 PowerMockito.whenNew()
到 return 部分模拟(间谍)也将不起作用,因为 Singleton
class 不提供无参数构造函数。
能否以任何其他方式实现所需的模拟?非单例可以吗classes?
首先,你可以对带有一些参数的构造函数的对象使用 whenNew:
@RunWith(PowerMockRunner.class)
@PrepareForTest(Singleton.class)
public class SingletonPrivateNewTest {
@Mock
Singleton singletonMock;
@Before
public void setUp() throws Exception {
PowerMockito.whenNew(Singleton.class)
.withAnyArguments()
.thenReturn(singletonMock);
}
@Test
public void testMockNew() throws Exception {
Mockito.when(singletonMock.process(anyString(), anyString())).thenReturn("sasa");
Foreign.main(new String[0]);
}
}
其次,为什么不用 stub 而不是 new:
@RunWith(PowerMockRunner.class)
@PrepareForTest(Singleton.class)
public class SingletonPrivateNewTest {
@Test
public void testMockNew() {
PowerMockito.mockStatic(Singleton.class);
Singleton singletonMock = Mockito.mock(Singleton.class);
PowerMockito.when(Singleton.getInstance(any())).thenReturn(singletonMock);
Mockito.when(singletonMock.process(anyString(), anyString())).thenReturn("sasa");
Foreign.main(new String[0]);
}
}
三、拦截进程方法:
- 创建真正的单例
- 创建模拟单例
- mock static getInstance 到 return 模拟。注意:您必须在获取真实实例后调用 mockStatic。
- 使用 thenAnswer 检查
process
调用的参数
- return 如果匹配所需的模式,则需要答案
- 否则在真正的单例上调用真正的方法
@RunWith(PowerMockRunner.class)
@PrepareForTest(Singleton.class)
public class SingletonPrivateNewTest {
@Test
public void testMockNew() {
var singletonReal = Singleton.getInstance(new Object());
var singletonMock = Mockito.mock(Singleton.class);
PowerMockito.mockStatic(Singleton.class);
PowerMockito.when(Singleton.getInstance(any())).thenReturn(singletonMock);
Mockito.when(singletonMock.process(anyString(), anyString())).thenAnswer((args) -> {
String a = args.getArgument(0);
String b = args.getArgument(1);
if ("alpha".equals(a) && "beta".equals(b)) {
return "sasa";
} else {
return singletonReal.process(a, b);
}
});
Foreign.main(new String[0]);
}
}
最后,使用间谍而不是模拟
@RunWith(PowerMockRunner.class)
@PrepareForTest(Singleton.class)
public class SingletonPrivateNewTest {
@Test
public void testMockNew() {
var singletonReal = Singleton.getInstance(new Object());
var singletonMock = Mockito.spy(singletonReal);
PowerMockito.mockStatic(Singleton.class);
PowerMockito.when(Singleton.getInstance(any())).thenReturn(singletonMock);
Mockito.when(singletonMock.process("alpha", "beta")).thenReturn("sasa");
// NOTE: real method is called for other args
Foreign.main(new String[0]);
}
}
有什么方法可以使用 Mockito
或 PowerMockito
来拦截对对象或至少是单例对象的非静态方法的调用?
以下 classes 提供了示例:
public class Singleton {
private static Singleton INSTANCE = null;
private Singleton(Object parameter) {}
public static Singleton getInstance(Object parameter) {
if (INSTANCE == null) {
INSTANCE = new Singleton(parameter);
}
return INSTANCE;
}
public String process(String a, String b) {
return (a + b);
}
// Other methods
}
public class Foreign {
private Foreign() {}
public static void main(String[] args) {
System.out.println(Singleton.getInstance(new Object()).process("alpha", "beta"));
}
}
Singleton
对象是在 Foreign
class 中创建的,不受某些测试代码(上面未显示)的控制。这两个class都不能修改。 objective 是拦截对测试代码中非静态 process()
方法的调用,以便对于某些值,不同的结果是 returned,例如通话
Singleton.getInstance(new Object()).process("alpha", "beta");
模拟为 return "alpha-beta"
而不是预期的 "alphabeta"
.
一个解决方案可能是拦截 Singleton.getInstance()
方法来实例化 Singleton 的自定义子 class,例如使用
public class SubSingleton extends Singleton {
public SubSingleton(Object parameter) {
super(parameter);
}
public String process(String a, String b) {
if ("alpha".equals(a) && "beta".equals(b)) {
return a + "-" + b;
}
return super.process(a + b);
}
}
然后,对 Singleton.process()
方法的调用将被拦截,如下所示:
Object parameter = new Object();
PowerMockito.doReturn(new SubSingleton(parameter)).when(Singleton.class, "getInstance", parameter);
但是上面的Singleton
class只提供了一个私有的构造函数,所以不能扩展。使用 PowerMockito.whenNew()
到 return 部分模拟(间谍)也将不起作用,因为 Singleton
class 不提供无参数构造函数。
能否以任何其他方式实现所需的模拟?非单例可以吗classes?
首先,你可以对带有一些参数的构造函数的对象使用 whenNew:
@RunWith(PowerMockRunner.class)
@PrepareForTest(Singleton.class)
public class SingletonPrivateNewTest {
@Mock
Singleton singletonMock;
@Before
public void setUp() throws Exception {
PowerMockito.whenNew(Singleton.class)
.withAnyArguments()
.thenReturn(singletonMock);
}
@Test
public void testMockNew() throws Exception {
Mockito.when(singletonMock.process(anyString(), anyString())).thenReturn("sasa");
Foreign.main(new String[0]);
}
}
其次,为什么不用 stub 而不是 new:
@RunWith(PowerMockRunner.class)
@PrepareForTest(Singleton.class)
public class SingletonPrivateNewTest {
@Test
public void testMockNew() {
PowerMockito.mockStatic(Singleton.class);
Singleton singletonMock = Mockito.mock(Singleton.class);
PowerMockito.when(Singleton.getInstance(any())).thenReturn(singletonMock);
Mockito.when(singletonMock.process(anyString(), anyString())).thenReturn("sasa");
Foreign.main(new String[0]);
}
}
三、拦截进程方法:
- 创建真正的单例
- 创建模拟单例
- mock static getInstance 到 return 模拟。注意:您必须在获取真实实例后调用 mockStatic。
- 使用 thenAnswer 检查
process
调用的参数- return 如果匹配所需的模式,则需要答案
- 否则在真正的单例上调用真正的方法
@RunWith(PowerMockRunner.class)
@PrepareForTest(Singleton.class)
public class SingletonPrivateNewTest {
@Test
public void testMockNew() {
var singletonReal = Singleton.getInstance(new Object());
var singletonMock = Mockito.mock(Singleton.class);
PowerMockito.mockStatic(Singleton.class);
PowerMockito.when(Singleton.getInstance(any())).thenReturn(singletonMock);
Mockito.when(singletonMock.process(anyString(), anyString())).thenAnswer((args) -> {
String a = args.getArgument(0);
String b = args.getArgument(1);
if ("alpha".equals(a) && "beta".equals(b)) {
return "sasa";
} else {
return singletonReal.process(a, b);
}
});
Foreign.main(new String[0]);
}
}
最后,使用间谍而不是模拟
@RunWith(PowerMockRunner.class)
@PrepareForTest(Singleton.class)
public class SingletonPrivateNewTest {
@Test
public void testMockNew() {
var singletonReal = Singleton.getInstance(new Object());
var singletonMock = Mockito.spy(singletonReal);
PowerMockito.mockStatic(Singleton.class);
PowerMockito.when(Singleton.getInstance(any())).thenReturn(singletonMock);
Mockito.when(singletonMock.process("alpha", "beta")).thenReturn("sasa");
// NOTE: real method is called for other args
Foreign.main(new String[0]);
}
}