Можно ли смоделировать один метод в уже существующем объекте?

Для интеграционного теста мне нужно смоделировать определенный метод в клиенте java-сервиса, не уничтожая остальную информацию в нем. У него нет собственного конструктора, поэтому о таком решении не может быть и речи:

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

Есть ли способ смоделировать метод deleteItem таким образом, чтобы учетные данные, конечные точки и т. Д. Были сохранены в существующем объекте DBClient?

редактировать: mockito не доступен для использования в этом случае

3 ответа

Решение

Вы можете использовать динамический прокси для перехвата любого вызова метода, который вы хотите, так что вы можете выбрать между вызовом реального метода или делать все, что вы хотите вместо этого.

Это пример того, как перехватить метод Set.add(), вы можете сделать то же самое для 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;
    }
}

Вы можете пойти по пути и создать подкласс класса DBClient. В этот подкласс передайте экземпляр DBClient, который вы хотите смоделировать.

Используйте композицию внутри подкласса и делегируйте все вызовы методов исходному DBClient, все, кроме того, который вы хотите смоделировать. Добавьте вашу ложную реализацию в метод, который вы хотите.

Это не так многоразово, как насмешливый фреймворк, но должно работать.

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

Надеюсь это поможет.

В Mockito 2+ вы можете использовать шпионскую функцию для этой цели:

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

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

Выход:

XXX
Другие вопросы по тегам