Mockito Basics — Mocking Dependencies in Java
Real classes have dependencies — databases, APIs, email services. You can’t call those in unit tests. Mockito creates fake versions of dependencies so you can test your logic in isolation.
Why Mocking?
UserService depends on:
├── UserRepository (database)
├── EmailService (sends emails)
└── AuditLogger (writes logs)
Unit test for UserService should NOT:
✗ Connect to a real database
✗ Send real emails
✗ Write real log files
Instead, MOCK all dependencies.
Maven Dependencies
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.12.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>5.12.0</version>
<scope>test</scope>
</dependency>
Your First Mock
The Code Under Test
public class UserService {
private final UserRepository userRepository;
private final EmailService emailService;
public UserService(UserRepository userRepository, EmailService emailService) {
this.userRepository = userRepository;
this.emailService = emailService;
}
public User createUser(String name, String email) {
if (userRepository.existsByEmail(email)) {
throw new DuplicateEmailException("Email already exists");
}
User user = new User(name, email);
User saved = userRepository.save(user);
emailService.sendWelcomeEmail(saved.getEmail());
return saved;
}
}
The Test
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserRepository userRepository;
@Mock
private EmailService emailService;
@InjectMocks
private UserService userService;
@Test
void shouldCreateUserSuccessfully() {
// Arrange
when(userRepository.existsByEmail("alice@test.com")).thenReturn(false);
when(userRepository.save(any(User.class)))
.thenReturn(new User(1L, "Alice", "alice@test.com"));
// Act
User result = userService.createUser("Alice", "alice@test.com");
// Assert
assertEquals("Alice", result.getName());
assertEquals(1L, result.getId());
verify(emailService).sendWelcomeEmail("alice@test.com");
}
}
Key Annotations
| Annotation | Purpose |
|---|---|
@ExtendWith(MockitoExtension.class) |
Enables Mockito annotations in JUnit 5 |
@Mock |
Creates a mock instance |
@InjectMocks |
Creates real instance, injects @Mock fields |
@Spy |
Partial mock (real methods unless stubbed) |
@Captor |
Captures arguments passed to mocks |
Mock Default Behavior
Mocks return safe defaults — no real logic executes:
| Return Type | Default |
|---|---|
Object |
null |
int, long, etc. |
0 |
boolean |
false |
Collection |
Empty collection |
Optional |
Optional.empty() |
String |
null |
@Test
void mockDefaultBehavior() {
// Nothing stubbed — returns defaults
assertNull(userRepository.findById(1L)); // null
assertFalse(userRepository.existsByEmail("x")); // false
assertEquals(0, userRepository.count()); // 0
}
Creating Mocks Without Annotations
// Option 1: Mockito.mock()
UserRepository mockRepo = mock(UserRepository.class);
EmailService mockEmail = mock(EmailService.class);
UserService service = new UserService(mockRepo, mockEmail);
// Option 2: @Mock annotations (preferred — less boilerplate)
@InjectMocks — How It Works
@Mock UserRepository userRepository;
@Mock EmailService emailService;
@InjectMocks UserService userService;
// Equivalent to: new UserService(userRepository, emailService)
Mockito tries injection in this order:
- Constructor injection (preferred)
- Setter injection
- Field injection
Best practice: Use constructor injection in your production code. It’s explicit and testable.
Mock vs Stub vs Fake
| Term | Definition | Example |
|---|---|---|
| Mock | Records interactions, verifiable | Mockito @Mock |
| Stub | Returns canned responses | when(...).thenReturn(...) |
| Fake | Working implementation (simplified) | In-memory database |
| Spy | Real object, selectively overridden | Mockito @Spy |
In practice, Mockito mocks are used for both mocking and stubbing.
Complete Example
@ExtendWith(MockitoExtension.class)
class OrderServiceTest {
@Mock private OrderRepository orderRepository;
@Mock private PaymentGateway paymentGateway;
@Mock private NotificationService notificationService;
@InjectMocks private OrderService orderService;
@Test
void shouldPlaceOrderSuccessfully() {
// Arrange
Order order = new Order("ORD-001", new BigDecimal("99.99"));
when(paymentGateway.charge(any(), any())).thenReturn(true);
when(orderRepository.save(any())).thenReturn(order);
// Act
Order result = orderService.placeOrder(order);
// Assert
assertNotNull(result);
verify(paymentGateway).charge(eq("ORD-001"), eq(new BigDecimal("99.99")));
verify(notificationService).sendOrderConfirmation("ORD-001");
verify(orderRepository).save(order);
}
@Test
void shouldThrowWhenPaymentFails() {
Order order = new Order("ORD-002", new BigDecimal("50.00"));
when(paymentGateway.charge(any(), any())).thenReturn(false);
assertThrows(PaymentFailedException.class,
() -> orderService.placeOrder(order));
verify(orderRepository, never()).save(any());
verify(notificationService, never()).sendOrderConfirmation(any());
}
}
Common Mistakes
| Mistake | Fix |
|---|---|
Forgetting @ExtendWith(MockitoExtension.class) |
Mocks stay null without it |
| Stubbing unused methods | Mockito strict mode will warn |
| Mocking the class under test | Mock dependencies, not the subject |
Using @InjectMocks with @Mock on same type |
They serve different purposes |
Key Takeaways
- Mock dependencies, not the class you’re testing.
@Mockcreates fakes,@InjectMockswires them together.- Mocks return safe defaults (null, 0, false, empty).
- Use constructor injection in production code for easy testability.
@ExtendWith(MockitoExtension.class)is required for JUnit 5.
What’s Next?
Next: Mocking and Stubbing — when/thenReturn, argument matchers, verify, void methods, and consecutive calls.