Java
Testing and Debugging Collections
Master collection testing with AssertJ and Hamcrest, debug ConcurrentModificationException and memory leaks, and build comprehensive test suites for collection-heavy code.
π At a Glance
| Aspect | Details |
|---|---|
| Topic | AssertJ assertions, Hamcrest matchers, debugging techniques, memory analysis |
| Complexity | Intermediate |
| Prerequisites | Part 1 (Collection Architecture), JUnit basics |
| Time to Master | 3-4 hours |
| Interview Frequency | Medium (testing knowledge valued) |
π― What You'll Learn
After completing this article, you will be able to:
- Write expressive collection tests with AssertJ
- Debug ConcurrentModificationException effectively
- Find and fix collection-related memory leaks
- Test thread-safety of concurrent collections
- Use IntelliJ debugger features for collection inspection
Production Story: The Test That Passed But Shouldn't Have
The Incident
Our test suite was green, but production had duplicates in what should be a unique collection:
JAVA(23 lines)CodeLoading syntax highlighter...
The Problem
TEXT(21 lines)CodeLoading syntax highlighter...
The Better Test
JAVA(32 lines)CodeLoading syntax highlighter...
Mental Model: Test Coverage Pyramid for Collections
TEXT(34 lines)CodeLoading syntax highlighter...
Deep Dive: AssertJ Collection Assertions
Basic Assertions
JAVA(14 lines)CodeLoading syntax highlighter...
Content Assertions
JAVA(21 lines)CodeLoading syntax highlighter...
Condition-Based Assertions
JAVA(19 lines)CodeLoading syntax highlighter...
Extracting Properties
JAVA(25 lines)CodeLoading syntax highlighter...
Filtering
JAVA(18 lines)CodeLoading syntax highlighter...
Map Assertions
JAVA(28 lines)CodeLoading syntax highlighter...
Deep Dive: Hamcrest Matchers
Collection Matchers
JAVA(19 lines)CodeLoading syntax highlighter...
Map Matchers
JAVA(6 lines)CodeLoading syntax highlighter...
Combining Matchers
JAVA(16 lines)CodeLoading syntax highlighter...
Deep Dive: Debugging Collection Issues
ConcurrentModificationException
JAVA(38 lines)CodeLoading syntax highlighter...
Memory Leak Detection
JAVA(44 lines)CodeLoading syntax highlighter...
Debugging with IntelliJ
JAVA(27 lines)CodeLoading syntax highlighter...
Deep Dive: Testing Thread Safety
Basic Concurrent Test
JAVA(21 lines)CodeLoading syntax highlighter...
Testing for Race Conditions
JAVA(38 lines)CodeLoading syntax highlighter...
Using JCStress (for serious concurrency testing)
JAVA(22 lines)CodeLoading syntax highlighter...
Deep Dive: Testing Edge Cases
Null Handling
JAVA(25 lines)CodeLoading syntax highlighter...
Empty Collection Handling
JAVA(17 lines)CodeLoading syntax highlighter...
Boundary Conditions
JAVA(33 lines)CodeLoading syntax highlighter...
Order Verification
JAVA(35 lines)CodeLoading syntax highlighter...
β οΈ Common Mistakes
Mistake 1: Not Testing Exact Content
JAVA(15 lines)CodeLoading syntax highlighter...
Mistake 2: Order Sensitivity in Set Tests
JAVA(14 lines)CodeLoading syntax highlighter...
Mistake 3: Shallow Collection Comparison
JAVA(17 lines)CodeLoading syntax highlighter...
Mistake 4: Not Testing Immutability
JAVA(17 lines)CodeLoading syntax highlighter...
Mistake 5: Flaky Concurrent Tests
JAVA(23 lines)CodeLoading syntax highlighter...
π» Exercises
Exercise 1: Collection Test Suite
Write comprehensive tests for this method:
JAVA(5 lines)CodeLoading syntax highlighter...
β
Solution:
JAVA(55 lines)CodeLoading syntax highlighter...
Exercise 2: Debug the Race Condition
Fix this thread-unsafe code and write a test that catches the bug:
JAVA(12 lines)CodeLoading syntax highlighter...
β
Solution:
JAVA(40 lines)CodeLoading syntax highlighter...
Exercise 3: Memory Leak Test
Write a test that detects if a cache is leaking memory:
JAVA(19 lines)CodeLoading syntax highlighter...
β
Solution:
JAVA(52 lines)CodeLoading syntax highlighter...
π€ Senior-Level Interview Questions
Question 1: contains() vs containsExactly()
Q: What's the difference between AssertJ's contains() and containsExactly()?
A:
JAVA(14 lines)CodeLoading syntax highlighter...
Question 2: Testing Thread Safety
Q: How do you test that a collection implementation is thread-safe?
A:
- Multiple threads hitting same operation:
JAVA(14 lines)CodeLoading syntax highlighter...
- Race condition tests with CountDownLatch:
JAVA(2 lines)CodeLoading syntax highlighter...
- JCStress for thorough analysis
- @RepeatedTest to catch intermittent failures
Question 3: Debugging ConcurrentModificationException
Q: How do you debug a ConcurrentModificationException that only happens in production?
A:
- Add logging around collection operations:
JAVA(5 lines)CodeLoading syntax highlighter...
- Check for multi-threaded access:
JAVA(3 lines)CodeLoading syntax highlighter...
- Use stack traces from the exception:
- The exception includes both the iterator creation and modification point
- Compare thread IDs in logs around those times
- Consider using CopyOnWriteArrayList temporarily to see if problem disappears
Question 4: Memory Leak Detection
Q: How would you detect if a Map is causing a memory leak?
A:
- Heap dump analysis:
BASH(2 lines)CodeLoading syntax highlighter...
- JFR recording:
BASHCodeLoading syntax highlighter...
- Test with assertions:
JAVA(7 lines)CodeLoading syntax highlighter...
- WeakHashMap test:
JAVA(5 lines)CodeLoading syntax highlighter...
Question 5: Test Isolation
Q: How do you ensure tests don't affect each other when testing shared collections?
A:
- Create fresh instances:
JAVA(4 lines)CodeLoading syntax highlighter...
- Use defensive copies:
JAVA(7 lines)CodeLoading syntax highlighter...
- Avoid static shared state
- Clear collections in @AfterEach if reusing
- Use @DirtiesContext in Spring if needed
π Summary & Key Takeaways
AssertJ Essentials
| Assertion | Use Case |
|---|---|
contains() | Element exists, others OK |
containsExactly() | Exact elements, exact order |
containsExactlyInAnyOrder() | Exact elements, any order |
extracting() | Test object properties |
filteredOn() | Filter then assert |
Test Coverage Areas
- Size: Empty, one, many, boundary
- Content: Expected, unexpected, duplicates, nulls
- Order: Insertion, sorted, reversed
- Mutation: Add, remove, update, clear
- Thread safety: Concurrent operations
- Memory: Growth, leaks
Debugging Tools
- IntelliJ Stream Trace
- Conditional breakpoints
- JFR and heap dumps
- WeakHashMap for leak tests
π Conclusion
Effective collection testing requires comprehensive coverage of edge cases and clear assertions. The key insights are:
- Use assertThat(list).containsExactly() for precise verification
- Test edge cases: empty, single, large, with nulls
- Verify immutability by attempting modification
- Use proper synchronization in concurrent tests
- Monitor memory for leak detection
In the next article, we'll explore Performance and Production Pitfalls - real-world issues and how to avoid them.
π Review Schedule
To solidify your understanding:
- Tomorrow: Rewrite one test using AssertJ
- In 3 days: Add edge case tests to existing code
- In 1 week: Run a concurrent test repeatedly
- In 2 weeks: Analyze heap dump for a production app
Next in Series: Part 28: Performance, Memory & Production Pitfalls
Series Navigation: Part 0: How to Use This Series | Full Index