22

I have the following situation:

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

I'm making unitests for Worker and obviously I want to mock Helper class so that his change method will always put 1 into k.

My real situation is more complicated but this code represents the issue. Thanks for help.

Pod
  • 3,938
  • 2
  • 37
  • 45
TryHarder
  • 289
  • 1
  • 4
  • 10

4 Answers4

21

I have a method with definition like this:

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

I will show you how you can change/modify the "out"(StringWriter) reference.

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));

Now when you do make the actual call like:

msgBodyTemplate.process(model, msgBodyWriter);

the value of Stringbuffer ref in msgBodyWriter will be "Email message"; irrespective of it earlier value.

omkar
  • 414
  • 4
  • 7
5

I think doAnswer is the best method dealing with void method where the method manipulates the given parameters.

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

Basically, after getting the arguments you can do whatever modification you want. There's a blog post explaining it a bit.

Will Hughes
  • 367
  • 2
  • 7
4

To @JB Nizet's point, yes, it's good to refactor for testability. Often refactoring to make code more testable leads to code that is better for other reasons - separation of concerns and such. It's not always possible to do. Say it's not your code, or you have some other requirement to leave it alone (because lots of other classes rely on it being the way it is) or whatever.

If I understand what you need to do, I think you can do it with a spy:

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);

Then use the spiedWorkerUT instead of the workerUnderTest to run your tests.

It's not always possible to avoid instantiating something you want to mock. For that, there is PowerMock.

Helper mockHelper = mock(Helper.class);
whenNew(Helper.class).withNoArguments().thenReturn(mockHelper);
jhericks
  • 5,833
  • 6
  • 40
  • 60
  • Thanks for input. What I actually trying to do is forcing `mockHelper` to change his behavior when `change` method is called from `Worker`. It's easy to achieve with inheriting some `mockHelper` from `Helper` and override `change` method, but maybe mockito can help me without new class? – TryHarder Nov 26 '11 at 09:27
  • I'm confused. What do you want to change? Does 'change' change the argument you are passing or change the state of Helper for some other calls? – jhericks Nov 26 '11 at 15:44
  • It changes the argument (`k`) – TryHarder Dec 02 '11 at 09:57
  • Integer is immutable, so that's not possible. Is the real method passing something that's mutable? – jhericks Dec 13 '11 at 20:41
3

I would change the method signature and make it take a Helper instance as argument. The caller would create the helper and pass it to the somework method. The test would pass a mock helper.

If this isn't possible, at least call a protected factory method to create the helper, and mock this factory method when testing the somework method in order to make it return a mock helper:

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();
    }
}
Bhesh Gurung
  • 50,430
  • 22
  • 93
  • 142
JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • 1
    Um.. Ok, that's an option. But what if I'm not able to change current code, or is it "right" to refactor real design in order to make tests creation easier? – TryHarder Nov 25 '11 at 20:37
  • 1
    Yes, it's right. Writing testable code is an important part of a good developer's work. And that's why IOC frameworks are so popular: it promotes a design which makes code testable. – JB Nizet Nov 25 '11 at 20:40
  • Thank you. I'll leave the thread maybe there still some option to make what I intended with mockito, just for learning purpose. Meanwhile i'll follow your advice. – TryHarder Nov 25 '11 at 21:12