mockito — Cursor rule: what it does, how to install
This rule guides Java unit tests written with Mockito and JUnit 5, from mock setup to argument checks. Use it when an agent creates or edits tests.
.cursor/rules/mockito.mdcWhat mockito does
The .cursor/rules/mockito.mdc file applies to **/*.java. It recommends integrating Mockito through @ExtendWith(MockitoExtension.class), using annotations to initialize mocks, and keeping each test focused on one behavior. Assert observable results rather than internal calls.
The guidance covers strict stubbing, ArgumentCaptor, choosing mocks over spies, and parameterized tests with @CsvSource. Remove unused stubs; reserve lenient() for specific cases. Mockito 5 supports mocking final classes, static methods, and constructors, though the rule advises restraint. The setup uses JUnit 5 and Mockito; it does not use the older JUnit 4 runner.
What it helps with
- configure Mockito for JUnit 5 tests
- separate distinct behaviors into focused tests
- inspect the exact object passed to a mock
- remove unused stubs that trigger exceptions
- replace repetitive cases with a parameterized test
Example prompts for the agent
- Write a Java unit test for this service using Mockito and JUnit 5, following this rule.
- Review these tests and remove unused stubs; split unrelated checks into separate tests.
- Add a parameterized test with
@CsvSourcefor these input values.
How to install mockito
Manually: put the files in place
.cursor/rules/mockito.mdcFrontmatter: description, globs, alwaysApply
Manually: put the files in place
.claude/rules/mockito.mdThe paths frontmatter field limits the files: move globs from the source into paths
AGENTS.md
Codex reads AGENTS.md: paste the rule text there
Manually: put the files in place
.github/instructions/mockito.instructions.mdThe applyTo field sets which files it applies to: move globs from the source into applyTo
Manually: put the files in place
.devin/rules/mockito.mdManually: put the files in place
.clinerules/mockito.mdStep by step, with commands for each platform: Cursor rules: .mdc files, four modes and what replaced .cursorrules
Questions about mockito
How do I install mockito in Cursor?
Create .cursor/rules/mockito.mdc and place the rule in that file.
What does the mockito rule do?
It guides Java unit testing with Mockito and JUnit 5, including mock setup, outcome checks, and stub handling.
What does mockito require?
The rule is for Java files and tests using JUnit 5 and Mockito. It uses MockitoExtension for integration.
Source text
mockito.mdc · CC0-1.0
---
description: This guide provides definitive best practices for writing robust, maintainable, and readable unit tests using Mockito with JUnit 5, focusing on modern patterns and common pitfalls.
globs: **/*.java
---
# Mockito Best Practices
Mockito is the indispensable mocking framework for Java unit testing. Adhere to these guidelines to ensure your tests are clear, reliable, and resistant to refactoring.
## 1. Standardize Test Setup with JUnit 5 & Annotations
Always use JUnit 5's `@ExtendWith` for Mockito integration and leverage annotations for mock initialization. This keeps your setup concise and automatic.
❌ **BAD: Manual setup or old JUnit 4 runner**
```java
// JUnit 4: @RunWith(MockitoJUnitRunner.class) - Obsolete
// Manual mock creation
UserRepository repo = mock(UserRepository.class);
UserService service = new UserService(repo);
```
✅ **GOOD: JUnit 5 `@ExtendWith` and annotations**
```java
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.InjectMocks;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
UserRepository userRepository; // Collaborator
@InjectMocks
UserService userService; // System Under Test (SUT)
// ... tests
}
```
## 2. One Test, One Behavior
Each test method must verify a single, distinct behavior. This makes tests easier to debug and provides clear documentation.
❌ **BAD: Multiple unrelated assertions in one test**
```java
@Test
void processOrder_createsUser_addsPoints_sendsEmail() {
// ... setup
orderService.placeOrder(orderRequest);
verify(userRepository).save(any(User.class));
verify(loyaltyService).addPoints(anyString(), anyInt());
verify(notificationService).sendEmail(anyString(), anyString());
}
```
✅ **GOOD: Focused tests**
```java
@Test
void shouldSaveUserWhenOrderIsPlaced() {
orderService.placeOrder(orderRequest);
verify(userRepository).save(any(User.class));
}
@Test
void shouldAddLoyaltyPointsForEligibleUsers() {
when(loyaltyPolicy.isEligible(any())).thenReturn(true);
orderService.placeOrder(orderRequest);
verify(loyaltyService).addPoints(anyString(), anyInt());
}
```
## 3. Verify Outcomes, Not Implementation Details
Only verify interactions that are part of the contract being tested. Avoid verifying internal method calls or private state.
❌ **BAD: Verifying internal helper method calls**
```java
// Assuming OrderService.placeOrder() internally calls calculateTax()
@Test
void shouldCalculateTaxWhenPlacingOrder() {
orderService.placeOrder(orderRequest);
verify(taxCalculator).calculateTax(any()); // Tax calculation is an internal detail
}
```
✅ **GOOD: Verifying the observable outcome**
```java
@Test
void shouldReturnOrderWithCorrectTotalIncludingTax() {
when(taxCalculator.calculateTax(any())).thenReturn(BigDecimal.valueOf(10));
Order result = orderService.placeOrder(orderRequest);
assertEquals(BigDecimal.valueOf(110), result.getTotalAmount()); // Verify the final amount
}
```
## 4. Handle `UnnecessaryStubbingException` Correctly
Mockito's strict stubbing (default with `MockitoExtension`) catches unused stubs. This prevents dead code in tests. Resolve it by removing unused stubs. Use `lenient()` only when absolutely necessary (e.g., in parameterized tests where some stubs might not be hit in all cases).
❌ **BAD: Silencing all strictness with `@MockitoSettings(strictness = Strictness.LENIENT)`**
```java
@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT) // Hides potential issues
class MyServiceTest {
// ...
@Test
void someTest() {
when(mockDependency.someMethod()).thenReturn("value"); // This stub is never used
// ... test logic that doesn't call someMethod()
}
}
```
✅ **GOOD: Remove unused stubs or use `lenient()` for specific stubs**
```java
@ExtendWith(MockitoExtension.class) // Strictness is default
class MyServiceTest {
// ...
@Test
void someTest() {
// No unused stubs here
// If a stub is conditionally used, make it lenient:
Mockito.lenient().when(mockDependency.optionalMethod()).thenReturn("optional");
// ...
}
}
```
## 5. Use `ArgumentCaptor` for Complex Argument Verification
When you need to inspect the exact values of objects passed to a mock, `ArgumentCaptor` is your tool.
❌ **BAD: Generic `any()` when specific content matters**
```java
verify(invoiceRepository).save(any(Invoice.class)); // Only checks type
```
✅ **GOOD: Capture and assert on the actual object's state**
```java
import org.mockito.ArgumentCaptor;
// ...
@Test
void shouldSaveInvoiceWithCorrectDetails() {
// ... call SUT method
ArgumentCaptor<Invoice> captor = ArgumentCaptor.forClass(Invoice.class);
verify(invoiceRepository).save(captor.capture());
Invoice savedInvoice = captor.getValue();
assertEquals("ORD-123", savedInvoice.getOrderId());
assertEquals(BigDecimal.valueOf(150.00), savedInvoice.getAmount());
}
```
## 6. Prefer Mocks over Spies
Use mocks for external, unmanaged dependencies where you control all behavior. Use spies only when you need partial real behavior of an object.
❌ **BAD: Spying on simple dependencies that should be fully mocked**
```java
@Spy
UserService userService = new UserService(userRepository); // Unnecessary complexity
```
✅ **GOOD: Mocking dependencies, using `@InjectMocks` for SUT**
```java
@Mock
UserRepository userRepository;
@InjectMocks
UserService userService; // SUT is a real instance, dependencies are mocked
```
## 7. Use Parameterized Tests for Similar Scenarios
Avoid duplicate test methods for different inputs. Parameterized tests keep your test suite DRY and maintainable.
❌ **BAD: Repetitive tests for different inputs**
```java
@Test void shouldReturnBronzeFor0Points() { /* ... */ }
@Test void shouldReturnSilverFor1000Points() { /* ... */ }
```
✅ **GOOD: Single parameterized test**
```java
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
// ...
class LoyaltyServiceTest {
@InjectMocks
LoyaltyService loyaltyService;
@ParameterizedTest
@CsvSource({
"0, BRONZE",
"1000, SILVER",
"5000, GOLD"
})
void shouldReturnCorrectTierForPoints(int points, String expectedTier) {
String actualTier = loyaltyService.calculateTier(points);
assertEquals(expectedTier, actualTier);
}
}
```
## 8. Mock Final Classes, Static Methods, and Constructors (with Caution)
Mockito 5 (and newer) supports mocking these without extra dependencies. Use this feature judiciously for testing legacy code or third-party APIs that were not designed for testability. Avoid it for your own code; design for testability instead.
```java
// Example (use sparingly for legacy/third-party code)
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.when;
import org.mockito.MockedStatic;
@Test
void shouldMockStaticMethod() {
try (MockedStatic<MyStaticUtil> mockedStatic = mockStatic(MyStaticUtil.class)) {
when(MyStaticUtil.getConstantValue()).thenReturn("MOCKED_VALUE");
assertEquals("MOCKED_VALUE", MyStaticUtil.getConstantValue());
}
}
```
© sanjeed5 · CC0-1.0 · sanjeed5/awesome-cursor-rules-mdc