Подтвердить что ты не робот

Методы обрушения, которые манипулируют параметрами с помощью mockito

У меня следующая ситуация:

class Worker {  
  public Integer somework() {  
      Integer k=0;  
      Helper h= new Helper();  
      h.change(k);  
      return k;  
    }
}

class Helper {
  public void change(Integer k) {
    //k = Some calcs
  }
}

Я делаю unitests для Worker и, очевидно, хочу издеваться над классом Helper, чтобы его метод change всегда помещал 1 в k.

Моя реальная ситуация сложнее, но этот код представляет проблему. Спасибо за помощь.

4b9b3361

Ответ 1

Я бы изменил сигнатуру метода и заставил использовать экземпляр Helper в качестве аргумента. Вызывающий вызывал бы помощника и передавал его методу somework. Тест прошел бы мимоходом.

Если это невозможно, по крайней мере вызовите защищенный метод factory для создания помощника и издевайтесь над этим методом factory при тестировании метода somework, чтобы он возвращал вспомогательный помощник:

class Worker {  
    public Integer somework(){  
        Integer k=0;  
        Helper h= createHelper();  
        h.change(k);  
        return k;  
    }

    // this method may be mocked when testing somework, to return a mock helper.
    protected Helper createHelper() {
        return new Helper();
    }
}

Ответ 2

У меня есть метод с определением вроде этого:

class Template{
   public void process(Object rootMap, StringWriter out){
 .......   
  }
 }

Я покажу вам, как вы можете изменить/изменить ссылку "out" (StringWriter).

private final Template mocktTemplate = mock(Template.class);
doAnswer(new Answer<StringWriter>() {

            public StringWriter answer(InvocationOnMock invocation)
                    throws Throwable {
                Object[] args = invocation.getArguments();
                if (args[1] instanceof StringWriter) {
                    StringWriter stringWriter = (StringWriter) args[1];
                    stringWriter.write("Email message");
                }
                return null;
            }
        }).when(this.mocktTemplate).process(anyObject(),any(StringWriter.class));

Теперь, когда вы делаете фактический вызов, например:

msgBodyTemplate.process(model, msgBodyWriter);

значение Stringbuffer ref в msgBodyWriter будет "Сообщение электронной почты"; независимо от его более раннего значения.

Ответ 3

To @JB Нижне точка, да, это хорошо для рефакторинга для проверки. Часто рефакторинг, чтобы сделать код более надежным, приводит к коду, который лучше по другим причинам - разделение проблем и тому подобное. Это не всегда можно сделать. Скажите, что это не ваш код, или у вас есть другое требование оставить его в покое (потому что многие другие классы полагаются на то, что это так) или что-то еще.

Если я понимаю, что вам нужно сделать, я думаю, вы можете сделать это со шпионом:

Worker workerUnderTest = new Worker();
Worker spiedWorkerUT = spy(workerUnderTest);
Helper mockHelper = mock(Helper.class);
when(spiedWorkerUT.createHelper()).thenReturn(mockHelper);
Integer actual = spiedWorkerUT.someWork();
verify(mockHelper).change(0);

Затем используйте spiedWorkerUT вместо workUnderTest для запуска ваших тестов.

Не всегда можно избежать создания экземпляра того, что вы хотите высмеять. Для этого существует PowerMock.

Helper mockHelper = mock(Helper.class);
whenNew(Helper.class).withNoArguments().thenReturn(mockHelper);

Ответ 4

Я думаю, что doAnswer - лучший метод работы с методом void, когда метод обрабатывает заданные параметры.

doAnswer(new Answer() {
  public Object answer(InvocationOnMock invocation) {
      Object[] args = invocation.getArguments();
      Mock mock = invocation.getMock();
      return null;
  }})
.when(mock).someMethod();

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