Как издеваться над Class ‹? расширяет List ›myVar в Mockito?

Я хочу поиздеваться над классом в Mockito. Затем будет выполнен вызов .newInstance (), который, как ожидается, вернет фактический экземпляр класса (и вернет имитацию в моем случае).

Если он был настроен правильно, я мог бы сделать:

ArrayList myListMock = mock(ArrayList.class);
when(myVar.newInstance()).thenReturn(myListMock);

Я знаю, что могу настроить его так, чтобы новый экземпляр класса ArrayList был имитацией (с использованием PowerMockito whenNew), просто интересно, есть ли способ имитировать этот тип объекта класса, чтобы мне не приходилось переопределять создание экземпляра ...

Ниже приведен реальный класс, который я пытаюсь имитировать, я не могу изменить структуру, определенную интерфейсом. Я ищу способ предоставить cvs при вызове инициализации.

public class InputConstraintValidator 
    implements ConstraintValidator<InputValidation, StringWrapper> {

    Class<? extends SafeString> cvs;

    public void initialize(InputValidation constraintAnnotation) {
        cvs = constraintAnnotation.inputValidator();
    }

    public boolean isValid(StringWrapper value, 
                   ConstraintValidatorContext context) {

        SafeString instance;
        try {
             instance = cvs.newInstance();
        } catch (InstantiationException e) {
            return false;
        } catch (IllegalAccessException e) {
            return false;
    }
}

person TheZuck    schedule 24.10.2012    source источник


Ответы (2)


Mockito разработан исключительно для имитации экземпляров объектов. Под капотом макетный метод фактически создает прокси-сервер, который принимает вызовы всех неокончательных методов, а также регистрирует и фиксирует эти вызовы по мере необходимости. Нет хорошего способа использовать Mockito для замены функции в самом объекте Class. Это оставляет вам несколько вариантов:

  1. У меня нет опыта работы с PowerMock, но кажется, что он разработан для имитации статических методов.

  2. В стиле внедрения зависимостей превратите свой статический фабричный метод в фабричный экземпляр. Поскольку похоже, что вы на самом деле не работаете с ArrayList, предположим, что ваш класс - FooBar:

    class FooBar {
      static class Factory {
        static FooBar instance;
        FooBar getInstance() {
          if (instance == null) {
            instance = new FooBar();
          }
          return instance;
        }
      }
      // ...
    }
    

    Теперь пользователь вашего класса может получить параметр new FooBar.Factory(), который создает ваш настоящий FooBar в стиле singleton (надеюсь, лучше и более потокобезопасен, чем мой простая реализация), и вы можете использовать чистый Mockito для имитации Factory. Если это похоже на много шаблонов, это потому, что это так, но если вы думаете о переходе на решение DI, например Guice вы можете урезать его много.

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

    public class InputConstraintValidator implements 
        ConstraintValidator<InputValidation, StringWrapper> {
      Class<? extends SafeString> cvs;
    
      public void initialize(InputValidation constraintAnnotation) {
        cvs = constraintAnnotation.inputValidator();
      }
    
      public boolean isValid(StringWrapper value,
          ConstraintValidatorContext context) {
        SafeString instance;
        try {
          instance = getCvsInstance();
        } catch (InstantiationException e) {
          return false;
        } catch (IllegalAccessException e) {
          return false;
        }
      }
    
      @VisibleForTesting protected getCvsInstance()
          throws InstantiationException, IllegalAccessException {
        return cvs.newInstance();
      }
    }
    
    public class InputConstaintValidatorTest {
      @Test public void testWithMockCvs() {
        final SafeString cvs = mock(SafeString.class);
        InputConstraintValidator validator = new InputConstraintValidator() {
          @Override protected getCvsInstance() {
            return cvs;
          }
        }
        // test
      }
    }
    
person Jeff Bowman    schedule 25.10.2012
comment
После пары месяцев опыта я могу сказать, что использую PowerMock для имитации создания экземпляра, и это довольно просто и понятно. - person TheZuck; 13.01.2013

Думаю, вам просто нужно ввести дополнительный макет для Class:

ArrayList<?> myListMock = mock(ArrayList.class);
Class<ArrayList> clazz = mock(Class.class);
when(clazz.newInstance()).thenReturn(myListMock);

Конечно, хитрость заключается в том, чтобы убедиться, что ваш mocked clazz.newInstance() не будет вызван повсюду, потому что из-за стирания типа вы не можете указать, что это действительно Class<ArrayList>.

Кроме того, будьте осторожны, определяя свой собственный макет для чего-то столь же фундаментального, как ArrayList - обычно я бы использовал «настоящий» и заполнял его макетами.

person millhouse    schedule 24.10.2012
comment
Я пробовал это, но он не компилировался ... может я что-то пропустил, попробую еще раз. - person TheZuck; 25.10.2012