Java
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented Java
-
Section 4: Collections Framework
-
Section 5: Exception Handling
-
Section 6: Generics
-
Section 7: Functional Java
-
Section 8: Concurrency
-
Section 9: I/O and NIO
-
Section 10: JVM Internals
-
Section 11: Modern Java Features
-
Section 12: Build Tools and Project Structure
-
Section 13: Testing
-
Section 14: Databases and Persistence
-
Section 15: Networking
-
Section 16: Design and Best Practices
-
Section 17: Reflection and Annotations
-
Section 18: Logging and Diagnostics
-
Section 19: Date, Time, and Internationalization
-
Section 20: Java Platform Module System
-
Section 21: Security in Java
-
Section 22: Advanced Collections and Data Structures
-
Section 23: More Concurrency Patterns
-
Section 24: Compression, Files, and System Integration
-
Section 25: GUI Programming
-
Section 26: Practical Projects
-
Section 27: Data Structures and Algorithms
-
Section 28: Interview and Algorithm Practice
-
Section 29: JSON and Data Interchange
-
Section 30: More Concurrency Utilities
-
Section 31: More Collections and Streams Practice
-
Section 32: More File and System Programming
-
Section 33: Standard Library Deep Dive
-
Section 34: More Practice and Drills
-
Section 35: More Testing and Quality
-
Section 36: More Design Patterns and Architecture
-
Section 37: Career and Ecosystem
-
Section 38: More OOP and Architecture Practice
-
Section 39: More Enterprise Concepts
-
Section 40: Advanced JavaFX
-
Section 41: More Interview Practice
126: Mocking with Mockito
Up until now, you've likely been writing tests that exercise your code in a straight line. You provide an input, the method runs, and you assert the output. That works great for pure logic, but in a professional Java codebase, your classes rarely live in a vacuum. They depend on other services—database wrappers, external APIs, or complex legacy calculators. This is where things get tricky. If you want to test a service that processes a payment, do you really want your unit test to hit a live Stripe API every time it runs?
The fragility of the "Real Deal"
The naive approach to testing dependencies is to just use the real objects. You instantiate your PaymentService and pass it a real PaymentGateway. On the surface, this feels like the "truest" test because it's exercising the actual system. But in practice, this is a nightmare. First, it's slow. Network calls take milliseconds or seconds, and when you have three thousand tests, those seconds turn into an hour-long build process that everyone hates.
Worse, your tests become "flaky." If the payment API goes down for maintenance, your build fails, even though your code hasn't changed a single line. You're no longer testing your logic; you're testing the uptime of a third-party vendor. Some developers try to solve this by writing "Fakes"—manual classes like FakePaymentGateway that implement the interface but just store data in a list. I've seen this a lot, and while it's better than hitting a live API, you end up spending half your time maintaining a parallel hierarchy of fake classes that eventually drift away from how the real API actually behaves.
// The naive way: Hard-coding a fake or using a real service
public class PaymentServiceTest {
@Test
void testProcessPayment() {
// If this is a real gateway, we might actually charge a card!
// If it's a manual fake, we had to write a whole separate class for it.
PaymentGateway gateway = new RealPaymentGateway();
PaymentService service = new PaymentService(gateway);
boolean result = service.processPayment(100.00);
assertTrue(result);
}
}
Cutting the cord with Mockito
This is where Mockito comes in. Instead of writing a manual fake or risking a real API call, we create a "mock"—a dynamic proxy of the class we need. I think of a mock as a stunt double. It looks like the real object and has the same methods, but it doesn't actually do anything unless you specifically tell it how to behave.
Using Mockito.mock(), you can create an instance of a dependency on the fly. Then, using the when().thenReturn() syntax, you define exactly what that dependency should do for this specific test case. This isolates your unit of work. You aren't testing if the PaymentGateway works (that's the gateway's own team's job); you're testing how PaymentService reacts when the gateway returns "Success" or "Insufficient Funds."
// The professional way: Using Mockito
import static org.mockito.Mockito.*;
public class PaymentServiceTest {
@Test
void testProcessPayment_Successful() {
// Create the stunt double
PaymentGateway mockGateway = mock(PaymentGateway.class);
PaymentService service = new PaymentService(mockGateway);
// Tell the mock how to behave for this specific scenario
when(mockGateway.charge(anyDouble())).thenReturn(true);
boolean result = service.processPayment(100.00);
assertTrue(result);
// Verify that the service actually called the gateway
verify(mockGateway).charge(100.00);
}
}
The danger of lying to your tests
Now, a word of caution. Mocking is a superpower, but it's easy to overdo it. I've seen developers mock almost everything in their test, and they end up with a suite of tests that all pass perfectly—while the actual application crashes in production. Why? Because they mocked the dependency to behave in a way that the real object never would.
If you mock a method to return a User object, but the real API actually returns null in that scenario, your test is lying to you. You've created a "perfect world" in your test suite that doesn't exist in reality. To avoid this, only mock the boundaries of your system (like APIs, Databases, or Message Queues). If you find yourself mocking a simple POJO or a utility class, you're probably over-mocking. Keep your mocks thin, and always ensure the behavior you're stubbing is based on the actual documentation of the service you're mimicking.
📋 Practical Task
Exercise: Mocking the Email Dispatcher in NotificationService
You are working on a NotificationService that sends alerts to users. It depends on an EmailClient interface. The NotificationService.sendAlert(String message) method should only call the EmailClient.sendEmail() method if the message is not null and not empty. If the message is empty, it should return false and not attempt to send an email.
Your Task: Write a JUnit test using Mockito to verify two scenarios:
- Scenario A: When a valid message is provided,
sendEmailis called exactly once and the method returnstrue. - Scenario B: When an empty string is provided,
sendEmailis never called and the method returnsfalse.
You will need to:
1. Mock the EmailClient.
2. Use when(...).thenReturn(...) to simulate a successful email send.
3. Use verify(...) to ensure the EmailClient was (or was not) interacted with.
There are no comments for now.