методы заглушки, которые манипулируют параметрами с mockito - PullRequest
18 голосов
/ 26 ноября 2011

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

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
  }
}

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

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

Ответы [ 4 ]

18 голосов
/ 22 марта 2013

У меня есть метод с таким определением:

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 будет равно «Email message»; независимо от этого ранее значение.

4 голосов
/ 24 августа 2015

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

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

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

4 голосов
/ 26 ноября 2011

К точке зрения @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 вместо workerUnderTest для запуска ваших тестов.

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

Helper mockHelper = mock(Helper.class);
whenNew(Helper.class).withNoArguments().thenReturn(mockHelper);
3 голосов
/ 26 ноября 2011

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

Если это невозможно, по крайней мере вызовите защищенный фабричный метод, чтобы создать хелпер, и имитируйте этот фабричный метод при тестировании метода 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();
    }
}
...