52

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?

ROMANIA_engineer
  • 54,432
  • 29
  • 203
  • 199
user657592
  • 1,031
  • 4
  • 16
  • 24
  • possible duplicate of [Mocking methods of local scope objects with Mockito](http://stackoverflow.com/questions/6520242/mocking-methods-of-local-scope-objects-with-mockito) – zubergu Apr 23 '14 at 06:28
  • 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. – user657592 Apr 23 '14 at 07:26

3 Answers3

48

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
}
Mureinik
  • 297,002
  • 52
  • 306
  • 350
  • 23
    I don't wish to write a protected createDateTime() method. Is there no other way to go about it? – user657592 Apr 23 '14 at 06:55
  • 4
    Actually you have to use Mockito.doReturn(dt).when(a).createDateTime() Please see https://stackoverflow.com/questions/11620103/mockito-trying-to-spy-on-method-is-calling-the-original-method – Alex Sep 27 '17 at 16:20
  • 2
    This is of course correct @Alex, thanks for noticing! Edited and fixed. – Mureinik Sep 27 '17 at 16:22
  • 2
    Cannot `spy()` a class when it does not have default constructor. Want to avoid it. – WesternGun May 16 '19 at 08:38
  • @WesternGun 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. – Mureinik May 16 '19 at 09:01
  • OK I meant `@Spy` of mockito. – WesternGun May 16 '19 at 09:02
  • 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. – mykey May 31 '23 at 23:08
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.

JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • This requires a jdk1.8, right? I am currently on 1.7 and am not planning to upgrade anytime soon. – user657592 Apr 23 '14 at 07:08
  • 2
    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. – JB Nizet Apr 23 '14 at 07:15
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.

Matt
  • 378
  • 5
  • 9