简体   繁体   English

是否可以在现有对象中模拟单个方法?

[英]Is it possible to mock a single method in an already existing object?

For an integration test, I need to mock a specific method in a java service client without destroying the rest of the information in it. 对于集成测试,我需要在java服务客户端中模拟特定方法,而不破坏其中的其余信息。 It has no self-constructor, so a solution like this is out of the question: 它没有自构造函数,所以这样的解决方案是不可能的:

private DBClient mockClient = new DBClient(alreadyExistingClient){
    @Override
    void deleteItem(Item i){
        //my stuff goes here
    }
};

Is there a way to mock the deleteItem method such that the credentials, endpoints, etc... are preserved in an existing DBClient object? 有没有办法模拟deleteItem方法,以便在现有的DBClient对象中保留凭据,端点等等?

edit: mockito is not available for use in this case 编辑:mockito在这种情况下不可用

You can use a Dynamic Proxy to intercept any method invocation you want, so you can decide between invoking the real method or do whatever you want instead. 您可以使用动态代理拦截所需的任何方法调用,因此您可以决定调用实际方法还是执行任何您想要的方法。

This is an example of how to intercept the method Set.add() , you can do exactly the same for deleteItem() 这是一个如何拦截方法Set.add()的例子,你可以对deleteItem()做同样的deleteItem()

package example.dynamicproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Set;

public class SetProxyFactory {

    public static Set<?> getSetProxy(final Set<?> s) {
        final ClassLoader classLoader = s.getClass().getClassLoader();
        final Class<?>[] interfaces = new Class[] {Set.class};
        final InvocationHandler invocationHandler = new InvocationHandler() {

            @Override
            public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {

                if (method.getName().equals("add")) {
                    System.out.println("add() intercepted");
                    // do/return whatever you want
                }

                // or invoke the real method
                return method.invoke(s, args);
            }
        };

        final Object proxy = Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);

        return (Set<?>) proxy;
    }
}

You could go lo-fi and create a sub-class of the DBClient class. 你可以去低保真并创建一个DBClient类的子类。 To this subclass, pass the instance of DBClient you want to mock. 对于此子类,传递要模拟的DBClient实例。

Use composition inside the sub-class, and delegate all method calls to the original DBClient, all except the one you want to mock. 在子类中使用组合,并将所有方法调用委托给原始DBClient,除了要模拟的那个之外的所有方法调用。 Add your mock implementation to the method you want. 将模拟实现添加到所需的方法。

This is not as reusable as a mocking framework, but should work. 这不像模拟框架那样可重用,但应该可行。

DBClient mockDbClient = new DBClient() {
     private DBClient dbClientDelegate;

     public void setDelegate(DBClient dbClient) {
         dbClientDelegate = dbClient;
    }

    //override all methods. 
    //delegate to the corresponding method of the dbClientDelegate instance

    //overide the method you want to mock, add asserts for method arguments
    //return mock data as appropriate

}

mockDbClient.setDelegate(preinstantiatedDbClient);
//inject mockDbClient to test class
//call test class / method

Hope this helps. 希望这可以帮助。

In Mockito 2+ you can use spy feature for this purpose: 在Mockito 2+中,您可以使用间谍功能实现此目的:

    PrintStream realSystemOut = System.out;
    realSystemOut.println("XXX");

    PrintStream mockedSystemOut = Mockito.spy(realSystemOut);
    Mockito.doNothing().when(mockedSystemOut).println(Mockito.anyString());
    mockedSystemOut.println("YYY");

Output: 输出:

XXX

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM