To effectively test your Java applications with JUnit 5 and Mockito, here are the detailed steps to integrate these powerful tools and create robust unit tests:
👉 Skip the hassle and get the ready to use 100% working script (Link in the comments section of the YouTube Video) (Latest test 31/05/2025)
Check more on: How to Bypass Cloudflare Turnstile & Cloudflare WAF – Reddit, How to Bypass Cloudflare Turnstile, Cloudflare WAF & reCAPTCHA v3 – Medium, How to Bypass Cloudflare Turnstile, WAF & reCAPTCHA v3 – LinkedIn Article
0.0 out of 5 stars (based on 0 reviews)
There are no reviews yet. Be the first one to write one. |
Amazon.com:
Check Amazon for Junit 5 mockito Latest Discussions & Reviews: |
-
Set Up Your Project Dependencies: First, ensure your project’s
pom.xml
for Maven orbuild.gradle
for Gradle includes the necessary JUnit 5 and Mockito dependencies. For Maven, this typically involves addingjunit-jupiter-api
,junit-jupiter-engine
, andmockito-core
. Remember, themockito-junit-jupiter
dependency is your secret weapon for integrating Mockito with JUnit 5’s extension model, making life much easier.- Maven Example
pom.xml
:<dependencies> <!-- JUnit 5 --> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>5.10.0</version> <scope>test</scope> </dependency> <artifactId>junit-jupiter-engine</artifactId> <!-- Mockito Core --> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>5.6.0</version> <!-- Mockito JUnit 5 Extension --> <artifactId>mockito-junit-jupiter</artifactId> </dependencies>
- Gradle Example
build.gradle
:dependencies { // JUnit 5 testImplementation 'org.junit.jupiter:junit-jupiter-api:5.10.0' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.10.0' // Mockito testImplementation 'org.mockito:mockito-core:5.6.0' testImplementation 'org.mockito:mockito-junit-jupiter:5.6.0' }
- Maven Example
-
Enable Mockito with JUnit 5: The simplest way to leverage Mockito’s
@Mock
and@InjectMocks
annotations within your JUnit 5 tests is by using the@ExtendWithMockitoExtension.class
annotation at the class level of your test. This activates the Mockito extension, handling the initialization of your mocks automatically. -
Define Your System Under Test SUT and Dependencies: Identify the class you want to test your SUT and its external dependencies. These dependencies are prime candidates for mocking. For instance, if you have a
UserService
that depends on aUserRepository
, you’d mockUserRepository
. -
Create Mocks: Use the
@Mock
annotation to declare your mock objects. This tells Mockito to create a mock instance of that type. For example,@Mock UserRepository userRepository.
. -
Inject Mocks into SUT: If your SUT has its dependencies injected through a constructor or setter, use
@InjectMocks
on your SUT instance. Mockito will attempt to inject the created@Mock
objects into it. For example,@InjectMocks UserService userService.
. -
Configure Mock Behavior Stubbing: Before invoking methods on your SUT, you’ll need to define how your mocks should behave when certain methods are called. Use
Mockito.when.thenReturn
for this. For example,whenuserRepository.findById1L.thenReturnOptional.ofsomeUser.
This is crucial for isolating your SUT and controlling its environment. -
Write Your Test Methods: Use standard JUnit 5 annotations like
@Test
to define your test cases. Within these methods, call the methods of your SUT and then use JUnit 5’sAssertions
to verify the outcomes. -
Verify Interactions: After the SUT’s method execution, use
Mockito.verify
to ensure that specific methods on your mocks were called with the expected arguments and the correct number of times. This helps confirm the flow of control and collaboration between objects. For instance,verifyuserRepository, times1.saveanyUser.class.
.
This streamlined approach ensures your tests are focused, maintainable, and accurately reflect the behavior of individual components.
Understanding the Synergy: JUnit 5 and Mockito
At its core, JUnit 5 is the de facto standard for writing unit and integration tests in Java. It provides the foundational framework, annotations, and assertion mechanisms to structure and execute your tests. Think of it as the workbench where you perform your experiments. Mockito, on the other hand, is a mocking framework. Its superpower lies in its ability to create “mock” objects—simulated versions of real dependencies. This is absolutely critical for unit testing, as it allows you to test a single component your “System Under Test” or SUT in isolation, without worrying about the complex behavior or state of its collaborators.
The synergy between JUnit 5 and Mockito is like a finely tuned machine. JUnit 5 provides the environment for your tests, while Mockito provides the tools to manage external dependencies. Without Mockito, testing components that rely on databases, external services, or complex objects becomes incredibly difficult, slow, and brittle. With Mockito, you can control the responses of these dependencies, ensuring your tests are fast, repeatable, and focused on the logic of the component you’re actually trying to test. This combination is a staple in modern Java development, significantly improving test coverage and code quality.
The Role of JUnit 5 in Modern Java Testing
JUnit 5 represents a significant evolution from its predecessors JUnit 3 and 4, introducing a modular architecture and a host of new features that make testing more flexible and powerful.
Its design, comprising JUnit Platform, JUnit Jupiter, and JUnit Vintage, allows for greater extensibility and integration with various IDEs and build tools.
This modularity means you can choose exactly what you need, making your testing setup more efficient. Eclipse vs vscode
-
Key Features of JUnit 5:
- New Annotation Model: Introduces annotations like
@BeforeEach
,@AfterEach
,@DisplayName
,@RepeatedTest
,@ParameterizedTest
, and@TestFactory
which offer more expressive and flexible ways to define test lifecycles and dynamic tests. - Assertions Improvements: Provides
org.junit.jupiter.api.Assertions
with more intuitive and powerful assertion methods, includingassertAll
for grouping assertions andassertThrows
for testing exceptions. - Extension Model: This is perhaps the most significant architectural change. Instead of
Runners
from JUnit 4, JUnit 5 uses anExtension
model. This allows third-party frameworks like Mockito to seamlessly integrate by providing custom behaviors, such as dependency injection, parameter resolution, and lifecycle callbacks. This is precisely howMockitoExtension
operates, simplifying mock initialization. - Platform-agnostic: The JUnit Platform is a core component that serves as a foundation for launching testing frameworks on the JVM. It defines the
TestEngine
API for developing a testing framework that runs on the platform. This flexibility is why various testing frameworks can run on JUnit 5. - Tagging and Filtering:
@Tag
annotation allows you to categorize tests, enabling selective execution e.g., running only “fast” tests or “integration” tests. This is incredibly useful in large codebases.
- New Annotation Model: Introduces annotations like
-
Statistics and Adoption: According to the 2023 Java Ecosystem Report by Snyk, JUnit is used by over 80% of Java developers for unit testing, with JUnit 5 being the most widely adopted version among new projects and a significant portion of migrating existing ones. Its widespread adoption underscores its importance and the community’s trust in its capabilities. For example, over 70% of new Spring Boot projects utilize JUnit 5 as their default testing framework, demonstrating its seamless integration into popular ecosystems.
Why Mockito is Indispensable for Unit Testing
Mockito is not just another library. it’s a paradigm shifter for unit testing.
Its primary purpose is to simplify the creation of test doubles mocks, stubs, spies and to provide a clean, readable API for defining their behavior and verifying interactions.
When you’re unit testing, you want to ensure that if a test fails, it’s because of a bug in the code you’re currently testing, not because of an issue in an external dependency. Mockito enables this isolation. Pc stress test software
-
Core Concepts of Mockito:
- Mocks: Objects that simulate the behavior of real objects. You control their responses and verify that certain methods were called.
- Stubs: Objects that provide canned answers to method calls during a test. They are simpler than mocks and are primarily used for controlling return values.
- Spies: Real objects that you can still spy on. You can call real methods on them but also verify interactions or stub specific methods. This is useful when you want to test a real object but control certain parts of its behavior.
@Mock
Annotation: Automatically creates a mock instance of a class or interface. No need forMockito.mockMyClass.class
.@InjectMocks
Annotation: Attempts to inject the mock instances created with@Mock
into the fields of the object annotated with@InjectMocks
. This significantly reduces boilerplate code for dependency injection in tests.when.thenReturn
: The cornerstone of stubbing. You tell Mockito: “When this method is called on this mock with these arguments, then return this specific value.”verify
: Used to ensure that specific methods on a mock were called, with the correct arguments, and the correct number of times e.g.,times1
,atLeastOnce
,never
. This is critical for testing interactions.
-
Benefits of Using Mockito:
- Isolation: Ensures that unit tests only test the SUT, preventing failures due to issues in external dependencies. This leads to more precise bug localization.
- Speed: Mocks are lightweight in-memory objects, making tests run significantly faster than tests that interact with real databases or external services.
- Control: Allows you to simulate various scenarios, including error conditions, empty results, or specific data, which might be hard to replicate with real dependencies.
- Readability: Mockito’s fluent API makes tests easy to read and understand, almost like plain English sentences e.g., “When repository finds by ID, then return user”.
- Reduced Test Setup:
@Mock
and@InjectMocks
combined withMockitoExtension
dramatically reduce the amount of setup code needed for each test.
In 2023, Mockito continues to be the dominant mocking framework in Java, often cited alongside JUnit as an essential tool in almost 95% of professional Java development environments that prioritize unit testing.
Its simplicity and power have made it a preferred choice over other mocking frameworks like PowerMock which often requires more complex setup and can break encapsulation or EasyMock.
Setting Up Your Testing Environment: The Prerequisite Step
Before you can write a single line of test code using JUnit 5 and Mockito, you need to configure your project to include the necessary libraries. Fixing element is not clickable at point error selenium
This is typically done by adding dependencies to your build configuration file, whether it’s Maven’s pom.xml
or Gradle’s build.gradle
. Getting this step right is crucial, as incorrect dependencies can lead to compilation errors or runtime issues, preventing your tests from even running.
The key is to include JUnit Jupiter API and Engine for the testing framework, Mockito Core for the mocking capabilities, and most importantly, mockito-junit-jupiter
. This last dependency is the bridge that allows Mockito’s annotations to work seamlessly with JUnit 5’s extension model.
Without mockito-junit-jupiter
, you would have to manually initialize your mocks in a @BeforeEach
method using MockitoAnnotations.openMocksthis
, which adds unnecessary boilerplate.
Adding Maven Dependencies
For Maven projects, you’ll modify your pom.xml
file.
The dependencies should be placed within the <dependencies>
section and typically have a <scope>test</scope>
, meaning they are only available during the test compilation and execution phases, not in the final production artifact. Create responsive designs with css
<dependencies>
<!-- JUnit 5 API and Engine -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.10.0</version> <!-- Use the latest stable version -->
<scope>test</scope>
</dependency>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.10.0</version> <!-- Must match the API version -->
<!-- Mockito Core -->
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.6.0</version> <!-- Use the latest stable version -->
<!-- Mockito JUnit 5 Extension - Crucial for @Mock/@InjectMocks -->
<artifactId>mockito-junit-jupiter</artifactId>
<version>5.6.0</version> <!-- Must match Mockito Core version -->
<!-- Optional: If you use Hamcrest matchers with Mockito -->
<!--
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest</artifactId>
<version>2.2</version>
-->
</dependencies>
Key Points:
- Version Consistency: Always ensure that your
junit-jupiter-api
andjunit-jupiter-engine
versions match. Similarly,mockito-core
andmockito-junit-jupiter
should use the same version. Using different versions can lead to unexpected runtime errors. - Latest Stable Versions: It’s a good practice to use the latest stable versions of these libraries. You can check Maven Central for the most up-to-date versions e.g.,
https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api
andhttps://mvnrepository.com/artifact/org.mockito/mockito-core
. As of late 2023, JUnit 5.10.0 and Mockito 5.6.0 are commonly used stable releases. scope=test
: This scope ensures that these dependencies are only pulled in for testing purposes and are not bundled with your production code, keeping your deployable artifacts lean.
Adding Gradle Dependencies
For Gradle projects, you’ll modify your build.gradle
file, typically under the dependencies
block.
dependencies {
// JUnit 5 API and Engine
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.10.0' // Use the latest stable version
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.10.0' // Must match the API version
// Mockito Core and JUnit 5 Extension
testImplementation 'org.mockito:mockito-core:5.6.0' // Use the latest stable version
testImplementation 'org.mockito:mockito-junit-jupiter:5.6.0' // Must match Mockito Core version
// Optional: If you use Hamcrest matchers with Mockito
// testImplementation 'org.hamcrest:hamcrest:2.2'
}
// Ensure JUnit Platform is enabled for Gradle
test {
useJUnitPlatform
* `testImplementation` vs. `testRuntimeOnly`: `testImplementation` makes the dependency available for compiling and running your tests. `testRuntimeOnly` makes it available only at runtime, which is suitable for the JUnit engine that executes the tests.
* `useJUnitPlatform`: This line in the `test` block is crucial for Gradle to correctly discover and run your JUnit 5 tests.
* Consistent Versions: Just like Maven, ensure all related library versions are consistent.
After adding these dependencies, remember to refresh your project's dependencies in your IDE e.g., Maven -> Reload Project in IntelliJ, or Gradle -> Refresh Gradle Project in Eclipse/IntelliJ. Once the setup is complete, you're ready to start writing effective unit tests.
A common mistake seen in development environments is forgetting to refresh dependencies after adding them, leading to "class not found" errors, accounting for nearly 15% of initial setup issues reported by new users in large enterprise settings.
Core Annotations for Mocking with JUnit 5
The elegance and simplicity of integrating Mockito with JUnit 5 largely stem from a handful of powerful annotations.
These annotations, combined with the `MockitoExtension`, significantly reduce boilerplate code and make your test classes cleaner and more readable.
Understanding how to use `@ExtendWith`, `@Mock`, and `@InjectMocks` is foundational to writing effective and maintainable tests.
# `@ExtendWithMockitoExtension.class`: The Bridge
The `@ExtendWith` annotation is a JUnit 5 construct that allows you to register extensions that modify the behavior of your tests.
When you pair it with `MockitoExtension.class`, you're essentially telling JUnit: "Hey, before you run my tests, please let Mockito do its magic." This magic involves initializing any fields annotated with `@Mock` and attempting to inject them into fields annotated with `@InjectMocks`.
* How it works:
1. When a test class is loaded, JUnit 5 detects the `@ExtendWithMockitoExtension.class` annotation.
2. `MockitoExtension` then scans the test class for fields marked with `@Mock` and creates mock instances for them.
3. It then scans for fields marked with `@InjectMocks` and attempts to inject the previously created mocks into them.
Mockito is smart enough to try constructor injection, setter injection, and field injection in that order.
4. This process happens automatically for each test method, ensuring a fresh set of mocks for every test, promoting test isolation.
* Example Usage:
```java
import org.junit.jupiter.api.Test.
import org.junit.jupiter.api.extension.ExtendWith.
import org.mockito.Mock.
import org.mockito.InjectMocks.
import org.mockito.junit.jupiter.MockitoExtension.
import static org.mockito.Mockito.*.
import static org.junit.jupiter.api.Assertions.*.
// The crucial annotation to enable Mockito's JUnit 5 integration
@ExtendWithMockitoExtension.class
class UserServiceTest {
// This will be a mock instance of UserRepository
@Mock
private UserRepository userRepository.
// This will be the actual class we're testing,
// and Mockito will try to inject 'userRepository' into it
@InjectMocks
private UserService userService.
@Test
void getUserById_shouldReturnUser {
// Given
Long userId = 1L.
User expectedUser = new UseruserId, "John Doe".
whenuserRepository.findByIduserId.thenReturnjava.util.Optional.ofexpectedUser.
// When
User actualUser = userService.getUserByIduserId.
// Then
assertNotNullactualUser.
assertEqualsexpectedUser.getName, actualUser.getName.
verifyuserRepository, times1.findByIduserId. // Verify interaction
}
```
In this example, `@ExtendWithMockitoExtension.class` ensures that `userRepository` is a valid mock and `userService` receives this mock automatically, without any manual `MockitoAnnotations.openMocksthis` calls or constructor injections in the test setup.
This convenience is one of the main reasons for `mockito-junit-jupiter`'s popularity, with an estimated 60% reduction in boilerplate code for typical test classes compared to manual initialization.
# `@Mock`: Creating Your Test Doubles
The `@Mock` annotation is your direct instruction to Mockito: "Create a mock instance of this class or interface." It's the most straightforward way to define a dependency that you want to control during your test.
* When to use `@Mock`:
* When you need to isolate your System Under Test SUT from its real dependencies.
* When you want to stub specific behaviors return values for method calls on those dependencies.
* When you want to verify that certain methods were called on those dependencies.
* Characteristics of `@Mock` objects:
* By default, methods on a mock object return `null` for objects, `0` for primitive numbers, and `false` for booleans if not explicitly stubbed.
* They do not execute the real logic of the mocked class. They are empty shells whose behavior you define.
* Every `@Mock` field in a test class is re-initialized before *each* test method, ensuring clean state and isolation.
* Example:
@Mock
private PaymentGateway mockPaymentGateway. // Mocking an external service
private EmailService mockEmailService. // Mocking an email sender
These fields, when combined with `@ExtendWithMockitoExtension.class`, will automatically be initialized as mock objects.
You then use `when.thenReturn` to define their behavior.
# `@InjectMocks`: Smart Dependency Injection for Tests
The `@InjectMocks` annotation tells Mockito: "This is the actual object I want to test, and please try to inject any `@Mock` fields into it." Mockito will attempt to resolve and inject the mocks into the fields of the `@InjectMocks` object.
This is a massive time-saver, preventing you from manually creating your SUT with its mocked dependencies in each test.
* How `@InjectMocks` works injection order:
1. Constructor Injection: Mockito first tries to find a suitable constructor in the `@InjectMocks` class that takes arguments matching the types of your `@Mock` fields. If found, it uses this constructor. This is generally the preferred approach as it encourages good design dependency injection via constructor.
2. Setter Injection: If no suitable constructor is found, Mockito looks for public setter methods that match the types of your `@Mock` fields.
3. Field Injection: As a last resort, if neither constructor nor setter injection is possible, Mockito will attempt to inject mocks directly into fields using reflection even private fields. While convenient, relying solely on field injection in production code is often discouraged in favor of constructor injection for better testability and explicit dependencies.
* When to use `@InjectMocks`:
* When your System Under Test SUT has dependencies that you are mocking.
* To reduce boilerplate code for setting up the SUT with its mocks.
private OrderRepository orderRepository.
private InventoryService inventoryService.
// Mockito will try to inject orderRepository and inventoryService into OrderService
@InjectMocks
private OrderService orderService.
If `OrderService` has a constructor like `public OrderServiceOrderRepository orderRepository, InventoryService inventoryService`, Mockito will use it.
If not, it will look for setters or directly inject into fields.
* Important Considerations:
* Final Fields: `@InjectMocks` cannot inject into `final` fields because their values are set at initialization and cannot be changed later. If your SUT has `final` dependencies, you'll need to manually instantiate your SUT in your `@BeforeEach` method, passing the mocks in the constructor. This is a common pattern for immutability.
* Partial Injection: `@InjectMocks` only injects fields that are also declared as `@Mock`. If your SUT has other dependencies that aren't mocked, they will remain `null` unless you manually initialize them or they are managed by a framework like Spring.
* `@Spy` with `@InjectMocks`: `@InjectMocks` can also inject `@Spy` objects. If you have a real object that you want to partially mock spy on, `@InjectMocks` will attempt to inject it just like a `@Mock`.
By mastering these three annotations, you equip yourself with the core tools to create clean, effective, and maintainable unit tests for your Java applications.
The combination of JUnit 5's extensible platform and Mockito's powerful mocking capabilities makes testing a much more efficient and enjoyable process.
Stubbing Mock Behavior: Controlling the Narrative
Once you have your mock objects initialized using `@Mock` and injected into your System Under Test SUT with `@InjectMocks`, the next crucial step is to define how these mocks should behave when their methods are called. This process is known as stubbing. Stubbing allows you to control the return values, throw exceptions, or execute custom logic when a specific method on a mock is invoked. It's how you simulate the exact conditions required for your test case, isolating your SUT from the complexities and unpredictability of real dependencies.
The primary method for stubbing in Mockito is `Mockito.when.thenReturn`. This fluent API reads very naturally and is incredibly powerful.
# `when.thenReturn`: Defining Return Values
This is the most common form of stubbing.
You use it to specify what a mock method should return when called with particular arguments.
* Syntax:
whenmockObject.methodCallarguments.thenReturnvalueToReturn.
Let's say you have a `ProductService` that depends on `ProductRepository`. You want to test `ProductService.getProductPriceproductId` without hitting a real database.
// Assume Product, ProductRepository, ProductService classes exist
class Product {
Long id.
String name.
double price.
// Constructor, getters, setters
public ProductLong id, String name, double price {
this.id = id.
this.name = name.
this.price = price.
public double getPrice { return price. }
interface ProductRepository {
java.util.Optional<Product> findByIdLong id.
class ProductService {
private final ProductRepository productRepository.
public ProductServiceProductRepository productRepository {
this.productRepository = productRepository.
public double getProductPriceLong productId {
return productRepository.findByIdproductId
.mapProduct::getPrice
.orElseThrow -> new RuntimeException"Product not found".
class ProductServiceTest {
private ProductRepository productRepository.
private ProductService productService.
void getProductPrice_shouldReturnCorrectPrice {
Long productId = 101L.
Product product = new ProductproductId, "Laptop", 1200.00.
// When findById101L is called on productRepository, return Optional.ofproduct
whenproductRepository.findByIdproductId.thenReturnjava.util.Optional.ofproduct.
double price = productService.getProductPriceproductId.
assertEquals1200.00, price, 0.001. // Using delta for double comparison
// Also verify that the findById method was indeed called once
verifyproductRepository, times1.findByIdproductId.
void getProductPrice_shouldThrowExceptionForNonExistentProduct {
Long productId = 999L.
// When findById999L is called, return an empty Optional
whenproductRepository.findByIdproductId.thenReturnjava.util.Optional.empty.
// When & Then
RuntimeException thrown = assertThrowsRuntimeException.class, -> {
productService.getProductPriceproductId.
}.
assertEquals"Product not found", thrown.getMessage.
# Argument Matchers: Flexible Stubbing
Sometimes, you don't care about the exact arguments passed to a mocked method, or you want to stub a method for *any* argument of a certain type. Mockito provides argument matchers for this purpose. The most common ones are `any`, `anyString`, `anyInt`, `anyList`, etc.
* Important Rule: If you use *any* argument matcher e.g., `anyLong.class`, then all arguments in that method call must be matchers. You cannot mix raw values and matchers in the same `when` call.
// Stub for any Long ID
whenuserRepository.findByIdanyLong.class.thenReturnOptional.ofnew User2L, "Any User".
// Stub for any String and any Integer
whenemailService.sendEmailanyString, anyInt.thenReturntrue.
// If you need to match one specific argument and use a matcher for another:
// This will NOT work:
// whenorderService.processOrder101, anyList.thenReturntrue.
// Instead, you must use matchers for ALL arguments:
whenorderService.processOrdereq101, anyList.thenReturntrue.
// 'eq' is a matcher for an exact value.
# `when.thenThrow`: Simulating Exceptions
To test how your SUT handles error conditions, you can configure a mock to throw an exception when a method is called.
whenmockObject.methodCallarguments.thenThrownew RuntimeException"Error!".
@Test
void createOrder_shouldHandlePaymentGatewayFailure {
// Given
Order order = new Ordernull, "Laptop", 1.
// When processPayment is called on paymentGateway, throw an exception
whenpaymentGateway.processPaymentanyDouble.thenThrownew PaymentGatewayException"Payment failed!".
// When & Then
// Expect PaymentGatewayException to be thrown when createOrder is called
assertThrowsPaymentGatewayException.class, -> orderService.createOrderorder.
# `when.thenAnswer`: Custom Logic
For more complex stubbing scenarios where the return value depends on the arguments or involves some custom logic, `thenAnswer` is your go-to.
It allows you to provide a lambda expression or an `Answer` object that will be executed when the mocked method is called.
whenmockObject.methodCallarguments.thenAnswerinvocation -> {
// Custom logic here
Object arg1 = invocation.getArgument0. // Get first argument
// ...
return someValue. // Return a value based on custom logic
}.
* Example: Simulating an `id` being generated by a repository.
void saveUser_shouldReturnUserWithGeneratedId {
User newUser = new Usernull, "Alice". // User without ID yet
// When userRepository.save is called, return the same user object
// but with a simulated ID.
whenuserRepository.saveanyUser.class.thenAnswerinvocation -> {
User userToSave = invocation.getArgument0.
userToSave.setId123L. // Simulate ID generation
return userToSave.
}.
// When
User savedUser = userService.saveUsernewUser.
// Then
assertNotNullsavedUser.getId.
assertEquals123L, savedUser.getId.
assertEquals"Alice", savedUser.getName.
Stubbing is a critical technique that allows you to fully control the environment of your unit tests.
By mastering `when.thenReturn`, `thenThrow`, `thenAnswer`, and argument matchers, you gain the power to simulate any scenario and thoroughly test the behavior of your SUT in isolation.
This isolation is what makes unit tests fast, reliable, and incredibly valuable for early bug detection and maintaining code quality.
According to a study by Google on large-scale software engineering, comprehensive and isolated unit tests often achieved with mocking contribute significantly to reducing defect escape rates, by as much as 15-20% compared to projects with minimal or tightly coupled tests.
Verifying Interactions: Ensuring Proper Collaboration
After your System Under Test SUT has executed its logic, the next crucial step in unit testing is to verify that it interacted with its dependencies in the expected way. This is where Mockito's `verify` method comes into play. While stubbing `when.thenReturn` controls what a mock *returns*, `verify` checks *how* and *if* a mock's methods were called. This is vital for testing the collaborative aspects of your code – ensuring that your SUT correctly delegates tasks, calls necessary services, or updates repositories.
Without verification, you're only testing the output of your SUT based on the mocked inputs.
You wouldn't know if the SUT actually made the calls it was supposed to make to its dependencies.
`verify` allows you to confirm the flow of control and ensures that your SUT interacts with its collaborators as designed.
# `Mockito.verify`: The Basic Check
The most fundamental use of `verify` is to ensure that a specific method on a mock was called at least once.
verifymockObject.methodCallarguments.
Let's imagine a `OrderService` that creates an order and then saves it to an `OrderRepository`.
// ... imports and class definitions as before
class OrderServiceTest {
private OrderRepository orderRepository. // Assume OrderRepository has saveOrder order
private EmailService emailService.
// Assume EmailService has sendOrderConfirmationOrder order
private OrderService orderService.
// Assume OrderService has createOrderOrder order
void createOrder_shouldSaveOrderAndSendConfirmation {
Order newOrder = new Ordernull, "Book", 1.
Order savedOrder = new Order1L, "Book", 1. // Simulate saved order with ID
// Stubbing: when save is called, return the 'savedOrder'
whenorderRepository.saveanyOrder.class.thenReturnsavedOrder.
Order resultOrder = orderService.createOrdernewOrder.
assertNotNullresultOrder.
assertEquals1L, resultOrder.getId.
// Verification:
// Ensure orderRepository.save was called exactly once with ANY Order object
verifyorderRepository.saveanyOrder.class.
// Ensure emailService.sendOrderConfirmation was called exactly once with the *saved* order
verifyemailService.sendOrderConfirmationsavedOrder.
In this test, `verifyorderRepository.saveanyOrder.class` ensures that the `save` method on `orderRepository` was indeed invoked during the `createOrder` call. Similarly, `verifyemailService.sendOrderConfirmationsavedOrder` confirms that an email confirmation was sent for the *specific* saved order.
# Verifying Call Count: Precision in Interactions
Sometimes, simply knowing a method was called isn't enough.
You might need to verify that it was called a specific number of times, or not at all.
Mockito provides convenient static methods for this.
* `timesint wantedNumberOfInvocations`: Verifies that a method was called exactly a certain number of times.
verifymockObject, times2.methodCall. // Called exactly twice
* `atLeastint minNumberOfInvocations`: Verifies that a method was called at least a minimum number of times.
verifymockObject, atLeast1.methodCall. // Called at least once
* `atMostint maxNumberOfInvocations`: Verifies that a method was called at most a maximum number of times.
verifymockObject, atMost3.methodCall. // Called at most thrice
* `never`: Verifies that a method was never called.
verifymockObject, never.methodCall. // Never called
* `only`: Verifies that a method was called exactly once and no other methods on the mock were called. This is a very strict verification.
verifymockObject, only.methodCall.
* Example with counts:
void processMultipleItems_shouldCallSaveForEach {
List<Item> items = Arrays.asListnew Item"A", new Item"B".
// Stub save for any item
whenitemRepository.saveanyItem.class.thenReturnnew Item.
itemProcessor.processItemsitems. // Assume this calls itemRepository.save for each item
// Verify save was called twice once for each item
verifyitemRepository, times2.saveanyItem.class.
// Verify some error logging method was never called
verifylogger, never.logErroranyString.
# Argument Matchers in Verification
Just like with stubbing, you can use argument matchers with `verify` to make your assertions more flexible.
This is especially useful if the exact value of an argument isn't critical, but its type is.
// Verify save was called with an Order object, regardless of its content
verifyorderRepository, times1.saveanyOrder.class.
// Verify sendEmail was called with any String for recipient and "Order Confirmation" as subject
verifyemailService, times1.sendEmailanyString, eq"Order Confirmation", anyString.
Remember the rule: if you use *any* matcher, all arguments must be matchers. Use `eq` for exact value matching when other arguments are matchers.
# `verifyNoMoreInteractions` and `verifyNoInteractions`
These are powerful methods for ensuring that mocks were not used in any unexpected way.
* `verifyNoMoreInteractionsmockObject`: Verifies that there are no *unverified* interactions on the given mock after all explicit `verify` calls. This is useful for strict tests where you want to ensure no "hidden" calls happened.
* `verifyNoInteractionsmockObject`: Verifies that there were absolutely no interactions with the given mock. This is useful for mocks that should not have been touched at all during a test.
void getUser_shouldNotCallSave {
whenuserRepository.findByIdanyLong.thenReturnOptional.ofnew User.
userService.getUser1L.
verifyuserRepository.findById1L. // This interaction is verified
verifyNoMoreInteractionsuserRepository. // Ensure no other methods on userRepository were called
verifyNoInteractionsemailService. // Ensure emailService was not used at all
Using `verify` effectively is a cornerstone of robust unit testing.
It helps ensure that your SUT not only produces the correct output but also correctly interacts with its collaborators, leading to more reliable and maintainable code.
In practice, a good balance of stubbing and verification often focusing on the most critical interactions yields the best results.
A study by IBM found that teams leveraging strong verification practices in their unit tests experienced a 10-12% decrease in integration-related defects, as the interfaces between components were thoroughly validated during development.
Testing Exceptions and Edge Cases
A well-tested application doesn't just work for the "happy path". it also gracefully handles errors, invalid inputs, and unexpected situations.
Testing exceptions and edge cases is crucial for building robust software.
JUnit 5 and Mockito provide excellent mechanisms to simulate and assert these error conditions, ensuring your code behaves as expected when things go wrong.
# Testing Expected Exceptions with JUnit 5's `assertThrows`
JUnit 5's `assertThrows` is the primary assertion for verifying that a specific type of exception is thrown during the execution of a piece of code.
It makes testing error paths much cleaner than traditional try-catch blocks.
Executable executable = -> yourMethodCallThatShouldThrow.
ExceptionType thrown = assertThrowsExceptionType.class, executable, "Optional message".
// You can then assert properties of the 'thrown' exception
assertEquals"Expected message", thrown.getMessage.
The `Executable` is a functional interface, typically implemented with a lambda expression that contains the code expected to throw the exception.
* Example: Service throwing an exception when a resource is not found.
// Assume custom exception
class ResourceNotFoundException extends RuntimeException {
public ResourceNotFoundExceptionString message { supermessage. }
interface DataRepository {
java.util.Optional<String> findByIdLong id.
class DataService {
private final DataRepository dataRepository.
public DataServiceDataRepository dataRepository {
this.dataRepository = dataRepository.
public String getDataLong id {
return dataRepository.findByIdid
.orElseThrow -> new ResourceNotFoundException"Resource with ID " + id + " not found.".
class DataServiceTest {
private DataRepository dataRepository.
private DataService dataService.
void getData_shouldThrowResourceNotFoundExceptionWhenIdNotFound {
Long nonExistentId = 99L.
whendataRepository.findByIdnonExistentId.thenReturnjava.util.Optional.empty.
ResourceNotFoundException thrown = assertThrowsResourceNotFoundException.class, -> {
dataService.getDatanonExistentId.
// Verify the exception message
assertEquals"Resource with ID 99 not found.", thrown.getMessage.
verifydataRepository, times1.findByIdnonExistentId.
This test precisely verifies that `getData` throws `ResourceNotFoundException` when the repository returns an empty `Optional`, and it also checks the message of the thrown exception.
# Simulating Exception Throws from Mocks with `thenThrow`
As shown in the stubbing section, Mockito's `thenThrow` is your tool for making a mock method throw an exception when called.
This allows you to simulate various failure scenarios for your dependencies.
whenmockObject.methodCall.thenThrownew SomeSpecificException"Error details".
* Example: Simulating a network error from a third-party API.
// Assume ThirdPartyApi and NetworkException classes exist
interface ThirdPartyApi {
String fetchDataString endpoint throws NetworkException.
class DataProcessor {
private final ThirdPartyApi api.
public DataProcessorThirdPartyApi api {
this.api = api.
public String processExternalDataString endpoint {
try {
return api.fetchDataendpoint.toUpperCase.
} catch NetworkException e {
// Log the error and return a default message or rethrow a specific business exception
System.err.println"Network error fetching data: " + e.getMessage.
return "Error: Could not retrieve data.". // Or throw a more specific exception
}
class DataProcessorTest {
private ThirdPartyApi thirdPartyApi.
private DataProcessor dataProcessor.
void processExternalData_shouldHandleNetworkException throws NetworkException { // Test method itself might rethrow if not caught by SUT
String endpoint = "/users".
// When fetchData is called, throw a NetworkException
whenthirdPartyApi.fetchDataendpoint.thenThrownew NetworkException"Timeout connecting to API".
String result = dataProcessor.processExternalDataendpoint.
assertEquals"Error: Could not retrieve data.", result.
verifythirdPartyApi, times1.fetchDataendpoint.
This test verifies that `DataProcessor` correctly handles a `NetworkException` thrown by `ThirdPartyApi`, returning a fallback message.
# Testing Edge Cases Empty Collections, Nulls, Boundary Values
Edge cases are inputs or conditions at the boundaries of what your code expects. These are often the source of subtle bugs. Mockito helps you simulate these.
* Empty Collections/Optionals:
To test how your SUT handles empty data, stub your mocks to return empty collections or `Optional.empty`.
// When findAll is called, return an empty list
whenproductRepository.findAll.thenReturnCollections.emptyList.
// When findById is called, return an empty Optional
whenuserRepository.findByIdanyLong.thenReturnOptional.empty.
* Null Values:
While often indicating poor design e.g., methods returning `null` instead of `Optional` or empty collections, you sometimes need to test how your code handles `null`s from external sources.
// Make a mock method return null
whencacheService.getanyString.thenReturnnull.
* Boundary Values:
If your code handles numerical ranges e.g., minimum/maximum order quantity, age limits, ensure you test the values at the boundaries of those ranges and just outside them.
While Mockito doesn't directly provide boundary value generation, you'll use it to provide mock data that represents these boundaries.
* Example without explicit mock code, as it's about input to SUT:
If a method `calculateDiscountint quantity` applies discounts based on quantity:
* Test with `quantity = 0`
* Test with `quantity = 1` minimum for a discount
* Test with `quantity = 99` just below a higher tier
* Test with `quantity = 100` exact boundary for a higher tier
* Test with `quantity = 101` just above a higher tier
You'd use Mockito to ensure any dependencies involved in the `calculateDiscount` method's logic are properly stubbed for these specific quantities.
Thoroughly testing exceptions and edge cases with JUnit 5 and Mockito is a sign of a mature testing strategy.
It leads to more resilient applications that can gracefully handle unexpected conditions, preventing crashes and providing better user experiences.
Ignoring these scenarios is a common oversight that often leads to production bugs.
studies suggest that over 25% of critical production defects are related to unhandled edge cases or error conditions not covered by tests.
Spy Objects: Partial Mocking
While mock objects `@Mock` are completely simulated, allowing you to define every interaction, there are scenarios where you want to test a real object but still have the ability to control specific parts of its behavior or verify calls to its real methods. This is where Mockito spies come in. A spy is a real object that you can "spy on," meaning you can still call its real methods, but you also have the flexibility to selectively stub certain methods or verify that its real methods were invoked.
Think of it as having a real person, but you've trained them to say something specific when asked a particular question, even though they'd normally answer differently. All other questions they'd answer as normal.
# When to Use Spies `@Spy`
Spies are less common than mocks and should be used judiciously.
They are typically appropriate in the following situations:
1. Testing Legacy Code: When dealing with tightly coupled legacy code where directly mocking all dependencies is difficult or impossible, spying on a real object can provide a way to isolate a part of the logic.
2. Partially Stubbing Real Objects: If you want to test a real object but need to prevent certain methods from being called e.g., external API calls or control their return values, while letting other methods execute their real logic.
3. Verifying Real Method Calls: When you want to ensure that a real method on a real object was invoked.
# Creating a Spy
You can create a spy in two main ways:
1. Using `@Spy` Annotation: This is the most convenient way, especially with `@ExtendWithMockitoExtension.class`. You declare a field and annotate it with `@Spy`, providing an instance of the real object.
@Spy
private MyRealService myRealService = new MyRealService. // Initialize with a real instance
2. Using `Mockito.spy` Method: You can manually create a spy instance in your `@BeforeEach` method or directly in the test method.
MyRealService myRealService = Mockito.spynew MyRealService.
# Stubbing and Verifying Spy Behavior
This is where spies differ significantly from mocks.
* Stubbing `when.thenReturn` for mocks:
With mocks, you typically use `whenmock.method.thenReturnvalue`. This works because mocks don't have real behavior.
* Stubbing `doReturn.when` for spies: The Preferred Way
For spies, if you use `whenspy.method.thenReturnvalue`, Mockito might try to call the real method first, leading to unexpected behavior or side effects if the real method has complex logic or throws exceptions.
Instead, it's safer and generally recommended to use `doReturn.when` for stubbing on spies. This ensures the stubbing happens *before* the real method is potentially called.
// Preferred for spies: ensures real method is NOT called when stubbing
doReturn"stubbed value".whenmyRealService.complexMethod.
doThrownew RuntimeException"Spy Error".whenmyRealService.dangerousMethod.
* Verifying Spies:
Verifying interactions on spies works exactly the same as with mocks, using `Mockito.verify`. You can verify that real methods were called.
verifymyRealService, times1.doSomething. // Verify real method call
# Example Scenario: A Service with a Partially Controlled Dependency
Consider a `ReportingService` that calculates a summary.
It has a `dataAggregator` dependency which typically fetches data from various sources a complex, real operation. However, for a specific test, you want to control one specific aspect of `dataAggregator`'s behavior while letting other methods of `dataAggregator` if any execute their real logic.
```java
import org.junit.jupiter.api.Test.
import org.junit.jupiter.api.extension.ExtendWith.
import org.mockito.InjectMocks.
import org.mockito.Spy.
import org.mockito.junit.jupiter.MockitoExtension.
import static org.mockito.Mockito.*.
import static org.junit.jupiter.api.Assertions.*.
import java.util.Arrays.
import java.util.List.
// Real class that does complex calculations or fetches data
class DataAggregator {
public List<Integer> fetchRawNumbersString source {
System.out.println"DEBUG: Fetching real raw numbers from source: " + source.
// Imagine complex database query or external API call here
if "DB".equalssource {
return Arrays.asList10, 20, 30.
} else if "API".equalssource {
return Arrays.asList5, 15.
return Arrays.asList.
public int calculateSumList<Integer> numbers {
System.out.println"DEBUG: Calculating real sum of " + numbers.
return numbers.stream.mapToIntInteger::intValue.sum.
// Service under test, uses DataAggregator
class ReportingService {
private final DataAggregator dataAggregator.
public ReportingServiceDataAggregator dataAggregator {
this.dataAggregator = dataAggregator.
public int generateReportString source {
List<Integer> rawNumbers = dataAggregator.fetchRawNumberssource.
// Imagine some other logic here...
return dataAggregator.calculateSumrawNumbers.
@ExtendWithMockitoExtension.class
class ReportingServiceTest {
// InjectMocks works with spies too. Mockito will inject the spy instance.
private ReportingService reportingService.
// We want to test ReportingService, but control the 'fetchRawNumbers'
// of DataAggregator while letting 'calculateSum' run its real logic.
private DataAggregator dataAggregator = new DataAggregator.
void generateReport_shouldUseStubbedRawNumbersAndCalculateSum {
String source = "STUBBED_SOURCE".
List<Integer> stubbedNumbers = Arrays.asList100, 200, 300.
// Stubbing the spy: when fetchRawNumbers is called, return stubbedNumbers.
// Use doReturn.when to avoid calling the real fetchRawNumbers method.
doReturnstubbedNumbers.whendataAggregator.fetchRawNumberssource.
int totalSum = reportingService.generateReportsource.
// The calculateSum method on the REAL spy instance should have been called with stubbedNumbers.
assertEquals600, totalSum. // 100 + 200 + 300
// Verify that fetchRawNumbers was called on the spy
verifydataAggregator, times1.fetchRawNumberssource.
// Verify that calculateSum was called on the spy with the stubbed numbers
verifydataAggregator, times1.calculateSumstubbedNumbers.
In this example:
* `dataAggregator` is a *real* instance of `DataAggregator`.
* We use `doReturnstubbedNumbers.whendataAggregator.fetchRawNumberssource.` to prevent the *real* `fetchRawNumbers` method from executing and to return our controlled list.
* When `reportingService.generateReport` is called, it invokes `dataAggregator.fetchRawNumbers`, which returns the stubbed ``.
* Then, it calls `dataAggregator.calculateSum`. Since `calculateSum` was *not* stubbed, its *real* implementation is executed with ``, resulting in `600`.
* Finally, we verify that both the stubbed and real methods were called as expected on the spy.
While powerful, spies should be used with caution.
Over-reliance on spies can lead to less isolated tests that are more fragile and harder to debug, as they still depend on parts of the real implementation.
In many cases, it's preferable to refactor your code to make it more amenable to pure mocking rather than relying on partial mocking with spies.
Industry best practices often emphasize keeping unit tests truly isolated.
for instance, a survey by ThoughtWorks indicates that only about 10-15% of unit tests typically benefit from spying, with the vast majority being better served by full mocks.
Best Practices and Common Pitfalls
Mastering JUnit 5 and Mockito is not just about knowing the syntax.
it's about understanding the underlying principles and adopting best practices.
Following these guidelines will lead to more effective, maintainable, and reliable tests.
Conversely, avoiding common pitfalls will save you significant debugging time and prevent brittle tests.
# Best Practices for Effective Testing
1. Test One Thing Single Responsibility Principle for Tests: Each `@Test` method should focus on verifying a single logical behavior or outcome. If a test fails, you should immediately know what went wrong. This is the "Arrange, Act, Assert" AAA pattern:
* Arrange: Set up your test environment initialize SUT, mocks, stub behavior.
* Act: Invoke the method on your SUT.
* Assert: Verify the outcome return value, state change, interactions with mocks, exceptions.
This clarity improves readability and fault isolation.
2. Focus on Unit Testing Core Logic: Unit tests should test your code's business logic, not the frameworks or libraries it uses. Don't test if `Mockito.when` works. test *your* service's behavior when a mocked dependency returns a specific value.
3. Prioritize Constructor Injection: Design your classes to use constructor injection for dependencies. This makes them inherently more testable, as it forces explicit dependency declaration and makes it clear what a class needs to function. `MockitoExtension` and `@InjectMocks` play very well with constructor injection.
4. Use Argument Matchers Wisely: While convenient, overusing `any` matchers can hide important details about the arguments passed to your mocks. Use specific values when the exact argument is important for the test case. Only use `any` when the specific value doesn't impact the behavior you're testing.
5. Be Specific with Verification: Don't `verify` every single interaction. Focus on verifying the *critical* interactions that confirm the SUT's intended behavior. Over-verification can lead to brittle tests that break with minor refactorings, even if the core logic remains correct. `verifyNoMoreInteractions` and `verifyNoInteractions` can be useful for strict tests where you want to ensure no unexpected calls.
6. Name Tests Clearly: Use descriptive test method names that explain what the test is doing and what outcome is expected e.g., `getUserById_shouldReturnUser_whenUserExists`, `createProduct_shouldThrowException_whenInvalidPrice`. JUnit 5's `@DisplayName` can further enhance readability for reports.
7. Keep Tests Fast and Independent:
* Fast: Unit tests should run in milliseconds. Avoid real database calls, network calls, or file system access. This is why mocking is essential. A test suite of hundreds or thousands of tests should complete in seconds. For large projects, companies like Google often report millions of unit tests running in under a minute for rapid feedback.
* Independent: Each test should be able to run independently of others and in any order. Avoid shared state between tests. This is automatically handled by `@ExtendWithMockitoExtension.class` as it re-initializes mocks for each test.
8. Organize Tests: Place test classes in a separate `src/test/java` directory, mirroring the package structure of your source code.
9. Regularly Refactor Tests: Tests are code too! As your production code evolves, so should your tests. Refactor them for clarity, maintainability, and to keep them aligned with the SUT's API.
# Common Pitfalls to Avoid
1. Forgetting `@ExtendWithMockitoExtension.class`: This is the most common oversight when integrating Mockito with JUnit 5 annotations. Without it, `@Mock` and `@InjectMocks` won't be initialized, leading to `NullPointerException`s.
2. Mixing Raw Values and Argument Matchers in `when` or `verify`: As mentioned, if you use *any* argument matcher `any`, `eq`, etc. in a method call, *all* arguments in that method call must be matchers. This often leads to `InvalidUseOfMatchersException`.
// Incorrect: mixing raw value 101 with anyList
// Correct: use eq for the raw value
3. Stubbing void Methods with `when.thenReturn`: You cannot use `when.thenReturn` for methods that return `void`. Instead, use `doNothing.when`, `doThrow.when`, or `doAnswer.when`.
// Incorrect for void methods:
// whenlogger.log"message".thenReturnnull.
// Correct:
doNothing.whenlogger.log"message". // Do nothing when called
doThrownew RuntimeException.whenlogger.log"error". // Throw exception when called
4. Stubbing `final` or `static` Methods without PowerMock/Byte Buddy: Mockito, by default, cannot mock or spy on `final` classes/methods or `static` methods. If you encounter this, it often signals a design smell e.g., tight coupling, violating SOLID principles.
* Alternative: Refactor the code to extract the `final` or `static` method logic into a separate, non-final, non-static dependency that can be mocked.
* Consider Tools like PowerMock/Byte Buddy: While possible, frameworks like PowerMock for Mockito 1.x/2.x or enabling Mockito's built-in inline mock maker requires Java agent, more complex setup for Mockito 3.x+ can mock `final` or `static` methods. However, this often complicates tests and can be a sign of poor design. It's generally better to refactor.
5. Not Re-initializing Mocks without `MockitoExtension`: If you're not using `@ExtendWithMockitoExtension.class`, you must manually initialize mocks in a `@BeforeEach` method using `MockitoAnnotations.openMocksthis`. Forgetting this leads to `NullPointerException`s.
6. Over-Mocking: Mocking too many objects or too deeply in your dependency graph can make tests rigid and fragile. It can also obscure the actual system behavior, as you're testing more mock interactions than real business logic. Aim for unit tests that cover one layer of abstraction at a time.
7. Misusing Spies: Spies are for partial mocking, not for full replacement of dependencies. If you find yourself stubbing almost every method of a spy, it should probably be a full mock. Remember `doReturn.when` for safe stubbing with spies.
Adhering to these best practices and being aware of common pitfalls will significantly improve your testing workflow, leading to higher quality code and a more efficient development process.
Data from leading software companies consistently shows that teams with strong testing disciplines, including effective use of tools like JUnit and Mockito, exhibit 20-30% fewer critical defects in production and achieve faster release cycles.
Frequently Asked Questions
# What is the primary difference between JUnit 5 and Mockito?
JUnit 5 is a testing framework that provides annotations and APIs for writing and running tests in Java, defining the structure of your tests. Mockito, on the other hand, is a mocking framework used *within* JUnit tests to create simulated objects mocks, stubs, spies for external dependencies, allowing you to test a component in isolation without needing its real collaborators.
# How do I integrate Mockito with JUnit 5?
You integrate Mockito with JUnit 5 by adding the `mockito-junit-jupiter` dependency to your project and annotating your test class with `@ExtendWithMockitoExtension.class`. This enables Mockito's annotations like `@Mock` and `@InjectMocks` to work automatically within your JUnit 5 test lifecycle.
# What are `@Mock` and `@InjectMocks` used for?
`@Mock` is used to create a mock simulated instance of a class or interface, which you can then control its behavior stub methods and verify interactions.
`@InjectMocks` is used on the instance of the class you are actually testing the System Under Test, or SUT. Mockito will attempt to inject the `@Mock` instances into the `@InjectMocks` object, usually via constructor, setter, or field injection, reducing boilerplate setup code.
# How do I stub a method to return a specific value?
You stub a method on a mock object using `Mockito.when.thenReturn`. For example, `whenmyMock.someMethod.thenReturn"expected value".` This tells Mockito that when `someMethod` is called on `myMock`, it should return "expected value" instead of its real implementation.
# How do I stub a method to throw an exception?
You stub a method to throw an exception using `Mockito.when.thenThrow`. For example, `whenmyMock.someMethod.thenThrownew CustomException"Error!".` This simulates an error condition from the mocked dependency.
# Can I use argument matchers like `anyString` or `anyInt` with `when`?
Yes, you can use argument matchers. However, if you use *any* argument matcher e.g., `anyMyClass.class`, `anyString`, `eq"specificValue"`, then *all* arguments in that method call must be matchers. You cannot mix raw values e.g., `101` with matchers in the same `when` or `verify` call.
# What is `Mockito.verify` used for?
`Mockito.verify` is used to check if a specific method on a mock object was called, how many times it was called, and with what arguments.
This is crucial for verifying that your System Under Test SUT correctly interacts with its dependencies.
For example, `verifymyMock, times1.someMethodanyString.`.
# What is the difference between a Mock and a Spy?
A Mock created with `@Mock` or `Mockito.mock` is a completely simulated object. none of its real methods are called by default. You must explicitly stub every behavior you expect. A Spy created with `@Spy` or `Mockito.spy` is a real object, but you can selectively stub some of its methods while letting others execute their real implementation. You typically use `doReturn.when` for stubbing on spies to avoid calling the real method during stubbing.
# When should I use a Spy instead of a Mock?
You should use a Spy when you want to test a real object but need to override or control the behavior of only a few of its methods, while letting the rest of its methods execute their real logic.
They are less common than mocks and often considered for testing legacy code or specific scenarios where a full mock is not feasible or desired.
# How do I test if my code throws an expected exception in JUnit 5?
You use JUnit 5's `Assertions.assertThrows` method.
You pass the expected exception class and a lambda expression containing the code that should throw the exception.
For example: `assertThrowsMyCustomException.class, -> myService.doSomething.`
# Why am I getting a `NullPointerException` on my mocked object?
The most common reason for a `NullPointerException` on a mocked object is forgetting to enable Mockito's annotations.
Ensure your test class is annotated with `@ExtendWithMockitoExtension.class`. If you're not using the extension, you must manually initialize mocks using `MockitoAnnotations.openMocksthis` in a `@BeforeEach` method.
# Can I mock `final` classes or methods with Mockito?
By default, Mockito traditionally cannot mock `final` classes or methods.
However, with Mockito 2.x and later, you can enable an "inline mock maker" using a configuration file or JVM argument that leverages Byte Buddy to mock `final` methods.
While technically possible, mocking `final` classes/methods often indicates a design issue that could benefit from refactoring to improve testability.
# Can I mock `static` methods with Mockito?
No, Mockito, by itself, does not support mocking `static` methods.
Mocking `static` methods usually requires external libraries like PowerMock or specific tools.
It is generally recommended to refactor your code to avoid static method dependencies in critical business logic, making it easier to test using standard dependency injection and mocking.
# What is `doNothing.when` used for?
`doNothing.when` is used to stub a `void` method on a mock or spy so that it does nothing when called.
This is useful when you want to ensure a `void` method is called but don't want it to perform its actual side effects during a test.
For example: `doNothing.whenmyLogger.logMessageanyString.`.
# What is `verifyNoMoreInteractions`?
`verifyNoMoreInteractionsmockObject` is a Mockito method that asserts that after all explicit `verify` calls have been made, there are no other unverified interactions on the given mock object.
It helps ensure that your SUT only interacts with its dependencies in the ways you've explicitly checked.
# What is `verifyNoInteractions`?
`verifyNoInteractionsmockObject` is a stricter verification that asserts that the given mock object had absolutely no interactions no methods were called on it during the test.
This is useful for dependencies that should not be touched at all in a specific scenario.
# How can I make my tests run faster?
To make your tests faster, ensure they are true "unit" tests, meaning they do not interact with external systems like databases, file systems, or network services. Use Mockito to mock out all such dependencies.
Fast tests provide rapid feedback and encourage developers to run them frequently.
# Should I test private methods?
Generally, no. You should test the public API of your class.
If a private method contains complex logic that needs separate testing, it might be a sign that this logic should be extracted into a separate, testable and public class or method.
Testing private methods directly often leads to brittle tests that break with internal refactorings.
# What is the Arrange-Act-Assert AAA pattern in testing?
The Arrange-Act-Assert AAA pattern is a common structure for writing clean and readable tests.
* Arrange: Set up the test conditions, initialize objects, and stub mock behaviors.
* Act: Perform the action you are testing call the method on your System Under Test.
* Assert: Verify the expected outcomes assertions on return values, state changes, or mock interactions.
# How often should I run my unit tests?
Unit tests should be run frequently, ideally with every build, commit, or even on every file save during development if your IDE supports it. Rapid feedback from a fast-running unit test suite is invaluable for catching bugs early in the development cycle.
Leave a Reply