我在修改游戏时遇到了问题。我需要修改一个方法返回的内容而不编辑该方法本身。我尝试用power-mock做这个无济于事。有没有人知道如何使用power-mock或任何其他字节码操作库,甚至任何标准库?我愿意使用任何库,只要许可证允许它用于我的目的。
我希望能够编辑静态方法的返回,以及在所有对象上返回非静态方法。
以下是我使用power-mock
尝试的内容import org.powermock.api.easymock.PowerMock;
import static org.easymock.EasyMock.expect;
public class BeanTest
{
public static void main(String[] args) throws Exception
{
Bean beanMock = PowerMock.createMock(Bean.class);
expect(beanMock.convert("world")).andReturn("WORLD");
System.out.println(beanMock.convert("world"));
}
}
public class Bean
{
protected String convert(String name)
{
throw new UnsupportedOperationException("not implemented yet");
}
}
答案 0 :(得分:3)
在java中,返回类型不是方法签名的一部分,因此您可以随意使用它。
有两种方法可以透明地实现这一目标:
编辑:
使用java动态代理(用于接口)或CGlib(用于类),您可以指向代理的接口,您可以从该接口与调用相交,对其执行某些操作,并将其委托给实际实现。要实现这一点,您必须控制依赖注入。 Here is a nice explanation
示例:
SomeInterface t = (SomeInterface) Proxy.newProxyInstance(SomeInterface.class.getClassLoader(),
new Class<?>[] {SomeInterface.class},
new TestInvocationHandler(new TestImpl()));
它的作用: TestInvocationHandler扩展了InvocationHandler接口。 TestImpl是一个实现SomeInterface的类。从SomeInterface调用方法时,它将以TestInvocationHandler结束。 InvocationHandler有一个方法调用,如下所示:
public Object invoke(Object proxy, Method method, Object[] args);
代理 - 对象的引用
方法 - 名为
args - 方法的参数
使用AspectJ,您可以使用切入点指定应该包含在around建议中的内容。 AspectJ的优点是您可以在没有任何显式编程的情况下操作已编译的代码或您自己的代码。
示例:
@Aspect
public class MyAspect {
@Around("execution(* org.example.yourMethod(..))")
public Object doNothing(ProceedingJoinPoint pjp) {
// You can call the method or ignore the call and do your logic
return pjp.proceed();
}
}
这个http://www.hubberspot.com/2012/12/how-to-implement-around-advice-using_12.html是一个很好的例子,说明如何在弹簧上使用AspectJ。也许它可以帮助你作为一个起点。
用CGlib解释它要困难得多,所以我用Java Dynamic Proxies来解释它。对于你的情况
// I suppose Bean implements IBean interface
IBean beanMock = (IBean) Proxy.newProxyInstance (
IBean.getClass().getClassLoader(),
new Class[] { IBean.class },
new InvocationHandler() {
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
if(method.getName().equals("convert")) {
if(args[0].toString().equals("world")) {
return "WORLD"
}
} else {
// Implement default case
}
}
});
System.out.println(beanMock.convert("world"));
答案 1 :(得分:0)
你非常接近。看起来您正在使用EasyMock的PowerMock风格。我对Mockito的味道比较熟悉,所以我编写了一个简单的单元测试,根据你的尝试进行测试:
import static org.junit.Assert.assertEquals;
import static org.powermock.api.mockito.PowerMockito.mock;
import static org.powermock.api.mockito.PowerMockito.when;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.modules.junit4.PowerMockRunner;
@RunWith(PowerMockRunner.class)
public class BeanTest {
@Test
public void testConvert() throws Exception {
String worldLower = "world";
String worldUpper = "WORLD";
Bean beanMock = mock(Bean.class);
when(beanMock.convert(worldLower)).thenReturn(worldUpper);
assertEquals(worldUpper, beanMock.convert(worldLower));
}
}
我做的唯一额外更改是将Bean
和BeanTest
放入单独的类文件中。