Using Mockito to mock a local variable of a method
Asked Answered
D

3

53

I have a class A that needs to the tested. The following is the definition of A:

public class A {
    public void methodOne(int argument) {
        //some operations
        methodTwo(int argument);
        //some operations
    }

    private void methodTwo(int argument) {
        DateTime dateTime = new DateTime();
        //use dateTime to perform some operations
    }
}

And based on the dateTime value some data is to be manipulated, retrieved from the database. For this database, the values are persisted via a JSON file.

This complicates things. What I need is to set the dateTime to some specific date while it is being tested. Is there a way I can mock a local variable's value using mockito?

Dreamworld answered 23/4, 2014 at 6:20 Comment(2)
possible duplicate of Mocking methods of local scope objects with MockitoPolysepalous
None of the other questions that I found on this subject had the extra layer of abstraction. Moreover, majority of them instantiated the local variable via some method of the mocked class.Dreamworld
Y
51

You cannot mock a local variable. What you could do, however, is extract its creation to a protected method and spy it:

public class A {
  public void methodOne(int argument) {
    //some operations
    methodTwo(int argument);
    //some operations
  }

  private void methodTwo(int argument) {
    DateTime dateTime = createDateTime();
    //use dateTime to perform some operations
  }

  protected DateTime createDateTime() {
    return new DateTime();
  }
}

public class ATest {
  @Test
  public void testMethodOne() {
    DateTime dt = new DateTime (/* some known parameters... */);
    A a = Mockito.spy(new A());
    doReturn(dt).when(a).createDateTime();
    int arg = 0; // Or some meaningful value...
    a.methodOne(arg);
    // assert the result
}
Youmans answered 23/4, 2014 at 6:27 Comment(7)
I don't wish to write a protected createDateTime() method. Is there no other way to go about it?Dreamworld
Actually you have to use Mockito.doReturn(dt).when(a).createDateTime() Please see #11620603Jasmine
This is of course correct @Alex, thanks for noticing! Edited and fixed.Youmans
Cannot spy() a class when it does not have default constructor. Want to avoid it.Lower
@Lower in the above snippet, you supply an instance of A you want to spy. It has nothing to do with a default constructor - you can construct this instance however you wish.Youmans
OK I meant @Spy of mockito.Lower
Thank you . This is right answer. But note , if the class that contains the method to be tested (in this case class A) is where we inject other variables i.e (@InjectMocks Private A a ; ), spying the createDateTime method won't work. Or Mockito won't allow you to do doReturn(dt).when(a).createDateTime(). The instance of A should be created with spy , A a = Mockito.spy(new A()); as posted in this answer.Poppycock
S
5

The best way to deal with such a problem is to use an injected Clock service, used to get new instances of DateTime. That way, your test can inject a mock Clock, which returns a specific DateTime instead of the current time.

Note that the new Java 8 time API defines such a Clock class, specifically for that purpose.

Shornick answered 23/4, 2014 at 6:29 Comment(2)
This requires a jdk1.8, right? I am currently on 1.7 and am not planning to upgrade anytime soon.Dreamworld
No. You just need to define your own Clock class or interface, and call clock.newDateTime() instead of new DateTime() to get the current time.Shornick
V
0

This might be too much of a hassle, but if you mock the object that can give you the local variable, you can return a mock of it. I'd rather not restructure the code to make testing easier, but its something to consider.

public class A {
    
    DateTimeFactory factory;

    private void method() {
        DateTime dateTime = factory.getDateTime();
        //use dateTime to perform some operations
    }
}

In your test you can do something like: when(factoryMock.getDateTime()).doReturn(dateTimeMock)

The factory mock would need to be injected into the class somehow.

Vassalage answered 7/8, 2019 at 21:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.