0
0
JUnittesting~15 mins

Stub objects in JUnit - Deep Dive

Choose your learning style9 modes available
Overview - Stub objects
What is it?
Stub objects are simple fake versions of real objects used in testing. They provide predefined responses to method calls, allowing tests to run without relying on complex or unavailable parts. Stubs help isolate the code being tested by replacing dependencies with controlled behavior. This makes tests faster and more reliable.
Why it matters
Without stub objects, tests would depend on real components that might be slow, unstable, or unavailable, making testing difficult and unreliable. Stubs solve this by simulating parts of the system, so tests focus only on the code under test. This leads to faster feedback and easier debugging, improving software quality.
Where it fits
Before learning about stub objects, you should understand unit testing basics and how dependencies affect tests. After stubs, you can learn about mocks and spies, which add verification of interactions, and then explore integration testing where real components are used together.
Mental Model
Core Idea
A stub object is a simple stand-in that returns fixed answers to let tests run without real dependencies.
Think of it like...
Using a stub object is like using a cardboard cutout of a friend to practice a conversation when the friend is not available.
┌───────────────┐       ┌───────────────┐
│ Test code     │──────▶│ Stub object   │
│ (calls method)│       │ (returns fixed│
│               │       │  response)    │
└───────────────┘       └───────────────┘
Build-Up - 7 Steps
1
FoundationUnderstanding dependencies in tests
🤔
Concept: Tests often need other parts of the system to work, called dependencies.
When you write a test for a piece of code, that code might call other objects or services. These are dependencies. For example, a method that sends an email depends on an email service. If the email service is slow or unavailable, the test can fail or be slow.
Result
You see that tests can be unreliable or slow if they depend on real components.
Knowing that dependencies can cause problems in tests helps you see why we need ways to replace them.
2
FoundationWhat is a stub object?
🤔
Concept: A stub object is a simple fake that returns preset answers to method calls.
Instead of using the real dependency, you create a stub that has the same methods but returns fixed data. For example, a stub email service might always say 'email sent' without actually sending anything.
Result
Tests run faster and more reliably because they don't depend on real components.
Replacing dependencies with stubs isolates the code under test and removes external factors.
3
IntermediateCreating stubs in JUnit tests
🤔Before reading on: do you think stubs are created by writing full classes or by simpler means? Commit to your answer.
Concept: In JUnit, stubs can be created by writing simple classes or using libraries to provide fixed responses.
You can write a small class that implements the needed interface and returns fixed values. For example: class EmailServiceStub implements EmailService { public boolean sendEmail(String to, String body) { return true; // always succeed } } Then use this stub in your test instead of the real service.
Result
Your test uses the stub and runs quickly without sending real emails.
Knowing how to create stubs manually helps you understand their role and limitations.
4
IntermediateUsing stub frameworks with JUnit
🤔Before reading on: do you think stub frameworks only replace method return values or also verify calls? Commit to your answer.
Concept: Stub frameworks like Mockito can create stubs quickly and flexibly, returning preset values for methods.
With Mockito, you can write: EmailService stub = Mockito.mock(EmailService.class); Mockito.when(stub.sendEmail(Mockito.anyString(), Mockito.anyString())).thenReturn(true); This creates a stub that returns true for any sendEmail call.
Result
Tests become easier to write and maintain with less boilerplate code.
Using stub frameworks speeds up test writing and reduces errors in stub creation.
5
IntermediateDifference between stubs and mocks
🤔Before reading on: do you think stubs check how methods are called or just return data? Commit to your answer.
Concept: Stubs provide fixed responses but do not verify how they are used; mocks can verify interactions.
A stub returns preset data to let tests run. A mock also records how it was called so tests can check if the code used it correctly. For example, a mock can check if sendEmail was called exactly once.
Result
You understand when to use stubs (simple replacement) versus mocks (interaction verification).
Knowing this difference helps you choose the right test double for your needs.
6
AdvancedLimitations and pitfalls of stub objects
🤔Before reading on: do you think stubs can catch all bugs in dependent code? Commit to your answer.
Concept: Stubs do not simulate real behavior fully and can hide bugs in dependencies.
Because stubs return fixed data, they do not behave like real objects in all cases. If the real dependency changes or has bugs, stubs won't catch those. Overusing stubs can lead to tests that pass but fail in production.
Result
You realize stubs are useful but must be used carefully and complemented with other tests.
Understanding stub limitations prevents false confidence in tests and encourages balanced testing strategies.
7
ExpertAdvanced stub usage in complex test scenarios
🤔Before reading on: do you think stubs can simulate exceptions or state changes? Commit to your answer.
Concept: Stubs can be programmed to simulate exceptions and state changes to test error handling and complex flows.
Using frameworks like Mockito, you can make stubs throw exceptions: Mockito.when(stub.sendEmail(Mockito.anyString(), Mockito.anyString())).thenThrow(new RuntimeException("Service down")); You can also program stubs to return different values on consecutive calls to simulate state changes.
Result
Tests can cover error cases and complex scenarios without real dependencies.
Knowing how to make stubs simulate complex behavior greatly expands your testing capabilities.
Under the Hood
Stub objects replace real dependencies by implementing the same interface or class but overriding methods to return fixed or programmed responses. At runtime, when the test calls a method on the stub, it executes the stub's code instead of the real one. This avoids side effects and external calls, making tests fast and predictable.
Why designed this way?
Stubs were designed to isolate the unit under test by removing dependencies that are slow, unreliable, or unavailable. Early testing faced challenges with complex dependencies, so stubs provided a simple way to simulate them. Alternatives like mocks and fakes evolved later to add verification and richer behavior.
┌───────────────┐       ┌───────────────┐       ┌───────────────┐
│ Test code     │──────▶│ Stub object   │──────▶│ Returns fixed │
│ calls method  │       │ intercepts    │       │ response      │
│ on dependency │       │ calls         │       │               │
└───────────────┘       └───────────────┘       └───────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Do stubs verify that methods were called correctly? Commit to yes or no before reading on.
Common Belief:Stubs check if the tested code called their methods correctly.
Tap to reveal reality
Reality:Stubs only return preset data; they do not verify how or if methods were called.
Why it matters:Confusing stubs with mocks can lead to missing bugs where the code calls dependencies incorrectly.
Quick: Can stubs fully replace real dependencies in all tests? Commit to yes or no before reading on.
Common Belief:Stubs can replace real dependencies in every test scenario without issues.
Tap to reveal reality
Reality:Stubs simplify dependencies but cannot simulate all real behaviors, especially complex logic or state changes.
Why it matters:Over-relying on stubs can cause tests to miss bugs that appear only with real dependencies.
Quick: Are stubs always handwritten classes? Commit to yes or no before reading on.
Common Belief:You must write stub classes manually for every dependency.
Tap to reveal reality
Reality:Modern frameworks can create stubs dynamically, reducing boilerplate and errors.
Why it matters:Not knowing about stub frameworks can make tests harder to write and maintain.
Quick: Do stubs slow down tests because they add extra code? Commit to yes or no before reading on.
Common Belief:Using stubs makes tests slower because of extra layers.
Tap to reveal reality
Reality:Stubs make tests faster by avoiding real external calls and side effects.
Why it matters:Misunderstanding this can discourage using stubs, leading to slow and flaky tests.
Expert Zone
1
Stubs should be as simple as possible but sometimes need to simulate complex behavior like exceptions or state changes to test edge cases.
2
Using stubs in combination with mocks allows separating concerns: stubs provide data, mocks verify interactions, improving test clarity.
3
Excessive use of stubs can lead to brittle tests tightly coupled to implementation details, making refactoring harder.
When NOT to use
Avoid stubs when you need to verify interactions or when the dependency's behavior affects the test outcome significantly; use mocks or fakes instead. For integration tests, use real components to catch integration issues.
Production Patterns
In real projects, stubs are used in unit tests to isolate code. Teams often use frameworks like Mockito to create stubs quickly. Stubs are combined with mocks for comprehensive testing. Continuous integration pipelines rely on stubbed tests for fast feedback.
Connections
Mocks
Mocks build on stubs by adding verification of method calls and interactions.
Understanding stubs clarifies the foundation of mocks, showing how test doubles evolve to cover more testing needs.
Dependency Injection
Stubs are often injected into code under test to replace real dependencies.
Knowing dependency injection helps you see how stubs fit into flexible, testable code design.
Theatre Rehearsal
Both involve practicing with stand-ins to prepare for real performance.
Seeing stubs as stand-ins like understudies in theatre highlights their role in safe, controlled testing environments.
Common Pitfalls
#1Using stubs that do too much and mimic real logic.
Wrong approach:class ComplexStub implements Service { public int calculate(int x) { return x * 2 + 5; // complex logic } }
Correct approach:class SimpleStub implements Service { public int calculate(int x) { return 10; // fixed value } }
Root cause:Confusing stubs with real implementations leads to complex, fragile tests.
#2Expecting stubs to verify method calls.
Wrong approach:Stub stub = new Stub(); // test assumes stub checks calls but it does not assertTrue(stub.wasCalled());
Correct approach:Mock mock = Mockito.mock(Service.class); // verify interaction explicitly Mockito.verify(mock).method();
Root cause:Misunderstanding the difference between stubs and mocks causes incorrect test assumptions.
#3Not updating stubs when real dependencies change.
Wrong approach:// stub returns old data Mockito.when(stub.getData()).thenReturn("old");
Correct approach:// update stub to match new behavior Mockito.when(stub.getData()).thenReturn("new");
Root cause:Ignoring dependency changes leads to tests passing incorrectly and missing bugs.
Key Takeaways
Stub objects replace real dependencies with simple, fixed-response stand-ins to isolate code under test.
They make tests faster and more reliable by removing external factors like slow or unavailable services.
Stubs only provide data and do not verify how methods are called; for that, mocks are needed.
Overusing or misusing stubs can hide bugs and create brittle tests, so balance is key.
Modern frameworks like Mockito simplify stub creation and allow simulating complex behaviors like exceptions.