编辑方法返回java的内容

时间:2013-03-19 22:22:49

标签: java powermock

我在修改游戏时遇到了问题。我需要修改一个方法返回的内容而不编辑该方法本身。我尝试用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");
   }
}

2 个答案:

答案 0 :(得分:3)

在java中,返回类型不是方法签名的一部分,因此您可以随意使用它。

有两种方法可以透明地实现这一目标:

  1. 如果您可以控制从中调用方法的对象的注入,请使用Java Dynamic代理。
  2. 将AspectJ方面与around建议一起使用,可以操纵不受控制的代码(第三方库)。使用AspectJ通过编译时编织或加载时编织
  3. 编辑:

    使用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)

你非常接近。看起来您正在使用EasyMockPowerMock风格。我对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));
    }
}

我做的唯一额外更改是将BeanBeanTest放入单独的类文件中。