Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Call order, static methods and constructors

This lesson covers three less common Mockito tools. InOrder checks not only that calls happened but also in which order. mockStatic replaces a static method for the duration of a test — for example, LocalDate.now(), which returns a different date every day. mockConstruction intercepts new. The last two are emergency tools: at the end we will see how to make code testable without them.

InOrder

publish() must first save the post and only then send the email — otherwise the author may get a notification about a post that failed to save. A plain verify does not see the order, InOrder does:

// src/test/java/com/example/blog/PostServiceInOrderTest.java
package com.example.blog;
 
import org.junit.jupiter.api.Test;
import org.mockito.InOrder;
 
import java.util.Optional;
 
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.*;
 
class PostServiceInOrderTest {
 
    private final PostRepository repository = mock(PostRepository.class);
    private final Notifier notifier = mock(Notifier.class);
    private final PostService service = new PostService(repository, notifier);
 
    @Test
    void savesBeforeNotifying() {
        when(repository.findById(1L))
                .thenReturn(Optional.of(new Post(1L, "Hello", "anna", false)));
 
        service.publish(1L);
 
        InOrder inOrder = inOrder(repository, notifier);
        inOrder.verify(repository).save(any(Post.class));
        inOrder.verify(notifier).send(anyString(), anyString());
    }
}

inOrder(...) takes the mocks whose calls are compared. The verify calls through it must match in the same order as in the code. If you swap the two lines, the test fails:

org.mockito.exceptions.verification.VerificationInOrderFailure:
 
Verification in order failure
Wanted but not invoked:
postRepository.save(
    <any com.example.blog.Post>
);
-> at com.example.blog.InOrderFailTest.notifiesBeforeSaving(InOrderFailTest.java:27)
Wanted anywhere AFTER following interaction:
notifier.send(
    "anna",
    "Your post "Hello" is published"
);

Check the order only where it matters for the behavior, as here.

mockStatic

The shop gives a 10% discount at weekends. The class takes the date itself:

// src/main/java/com/example/shop/WeekendDiscount.java
package com.example.shop;
 
import java.time.DayOfWeek;
import java.time.LocalDate;
 
public class WeekendDiscount {
 
    public int percent() {
        DayOfWeek day = LocalDate.now().getDayOfWeek();
        return day == DayOfWeek.SATURDAY || day == DayOfWeek.SUNDAY ? 10 : 0;
    }
}

A test of such code passes on Saturday and fails on Monday. mockStatic replaces LocalDate.now() (this needs the inline mock maker — the default in Mockito 5, with the agent set up in the first Mockito lesson):

// src/test/java/com/example/shop/WeekendDiscountStaticTest.java
package com.example.shop;
 
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
 
import java.time.LocalDate;
 
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mockStatic;
 
class WeekendDiscountStaticTest {
 
    @Test
    void tenPercentOnSaturday() {
        LocalDate saturday = LocalDate.of(2026, 10, 3);
 
        try (MockedStatic<LocalDate> mocked = mockStatic(LocalDate.class)) {
            mocked.when(LocalDate::now).thenReturn(saturday);
 
            assertEquals(10, new WeekendDiscount().percent());
        }
    }
}

Inside the try block all static methods of LocalDate are replaced; on leaving it, try-with-resources calls close() and the real methods come back. A second test, zeroOnMonday, does the same with Monday, 5 October 2026.

Note that the date is created before the block. Inside it LocalDate.of(...) is replaced too and returns null, so thenReturn(LocalDate.of(2026, 10, 3)) fails with UnfinishedStubbingException.

mockConstruction

mockConstruction makes every new of a class inside the block return a mock. For a class that creates its calculator itself:

// src/main/java/com/example/shop/Checkout.java
public class Checkout {
 
    public BigDecimal pay(BigDecimal price, int quantity) {
        PriceCalculator calculator = new PriceCalculator();
        return calculator.total(price, quantity);
    }
}
// src/test/java/com/example/shop/CheckoutTest.java
@Test
void usesPriceCalculator() {
    try (MockedConstruction<PriceCalculator> mocked = mockConstruction(PriceCalculator.class,
            (mock, context) -> when(mock.total(any(), anyInt())).thenReturn(new BigDecimal("1.00")))) {
 
        BigDecimal total = new Checkout().pay(new BigDecimal("12.00"), 3);
 
        assertEquals(new BigDecimal("1.00"), total);
        assertEquals(1, mocked.constructed().size());
    }
}

The second argument sets up each created mock; constructed() returns the mocks created so far.

[INFO] Running com.example.shop.CheckoutTest
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.099 s -- in com.example.shop.CheckoutTest
[INFO] Running com.example.shop.WeekendDiscountStaticTest
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.138 s -- in com.example.shop.WeekendDiscountStaticTest

Better: inject a Clock

mockStatic and mockConstruction hide a design problem: the class takes its dependencies itself. Pass them from outside, and no tricks are needed. For time Java has java.time.Clock:

// src/main/java/com/example/shop/WeekendDiscount.java
public class WeekendDiscount {
 
    private final Clock clock;
 
    public WeekendDiscount(Clock clock) {
        this.clock = clock;
    }
 
    public int percent() {
        DayOfWeek day = LocalDate.now(clock).getDayOfWeek();
        return day == DayOfWeek.SATURDAY || day == DayOfWeek.SUNDAY ? 10 : 0;
    }
}
// src/test/java/com/example/shop/WeekendDiscountTest.java
@Test
void tenPercentOnSaturday() {
    Clock saturday = Clock.fixed(Instant.parse("2026-10-03T12:00:00Z"), ZoneOffset.UTC);
 
    assertEquals(10, new WeekendDiscount(saturday).percent());
}

Clock.fixed always shows the given moment. The application passes Clock.systemDefaultZone(), the test passes a fixed clock — no mocks at all. In the same way, Checkout could receive a PriceCalculator in its constructor.

Common mistake

mockStatic without try-with-resources. The static mock stays active after the test, and the next test in the same thread cannot create its own:

MockedStatic<LocalDate> mocked = mockStatic(LocalDate.class);
mocked.when(LocalDate::now).thenReturn(saturday);
[ERROR] com.example.shop.StaticNoCloseTest.zeroOnMonday -- Time elapsed: 0.015 s <<< ERROR!
org.mockito.exceptions.base.MockitoException:
 
For java.time.LocalDate, static mocking is already registered in the current thread
 
To create a new mock, the existing static mock registration must be deregistered

Worse, until then every other test sees the fake date. Always open MockedStatic and MockedConstruction in try (...) — or call close() in @AfterEach.

Thanks for reading!

Donate to the site — we work hard for you.