7

I want to mock only the now() of LocalDate using PowerMock. It seems that it's ignoring the thenReturn value:

java.lang.AssertionError: 
Expected :2008
Actual   :2017

Test setup:

@PrepareForTest(LocalDate.class)
@RunWith(PowerMockRunner.class)
public class UserTest {

    @Test
    public void nowShouldReturnSameYear() throws Exception {
        LocalDate expected = LocalDate.parse("2008-04-04");

        PowerMockito.spy(LocalDate.class);
        when(LocalDate.now()).thenReturn(expected);

        Foo foo = new Foo();
        assertEquals(expected.getYear(), foo.getRightNow().getYear());
    }

Foo.java

public LocalDate getRightNow(){
        final LocalDate rightNow = LocalDate.now();
        return rightNow;
    }
richersoon
  • 4,682
  • 13
  • 44
  • 74

1 Answers1

9

You should PrepareForTest the Foo class:

@PrepareForTest(Foo.class)
@RunWith(PowerMockRunner.class)
public class ...

I'm not sure how spy behaves for static methods, so I prefer to use mockStatic:

PowerMockito.mockStatic(LocalDate.class);
when(LocalDate.now())....

To make the code more testable and don't depend on mocking static methods, you could also refactor your code to use a java.time.Clock:

public class Foo {

    private Clock clock;

    public Foo() {
        // use the system's default clock
        this(Clock.systemDefaultZone());
    }

    public Foo(Clock clock) {
        // use a custom clock
        this.clock = clock;
    }

    public LocalDate getRightNow() {
        final LocalDate rightNow = LocalDate.now(this.clock);
        return rightNow;
    }
}

If you don't specify a Clock, it'll use the system's default (exactly what the now() method does when called without parameters).

Then, in your test, you create a Clock that always return the same date and pass it to the Foo class. In this case, you won't need all the PowerMockito stuff:

public class UserTest {

    @Test
    public void nowShouldReturnSameYear() throws Exception {
        LocalDate parse = LocalDate.parse("2008-04-04");
        // create a fixed clock (it always return the same date, using the system's default timezone)
        ZoneId zone = ZoneId.systemDefault();
        Clock clock = Clock.fixed(parse.atStartOfDay(zone).toInstant(), zone);

        Foo foo = new Foo(clock);
        assertEquals(parse.getYear(), foo.getRightNow().getYear());
    }
}
  • I don't quite get the idea of the more testable example where `java.time.Clock` is used. The constructor that does not take a clock argument is not covered in any test. – Ruifeng Ma Mar 16 '19 at 08:04
  • Check this one for a more clear answer for using `Clock` https://stackoverflow.com/questions/32792000/how-can-i-mock-java-time-localdate-now – Asad Shakeel Aug 09 '21 at 07:37