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

AspectDetails
TopicAssertJ assertions, Hamcrest matchers, debugging techniques, memory analysis
ComplexityIntermediate
PrerequisitesPart 1 (Collection Architecture), JUnit basics
Time to Master3-4 hours
Interview FrequencyMedium (testing knowledge valued)

🎯 What You'll Learn

After completing this article, you will be able to:

  1. Write expressive collection tests with AssertJ
  2. Debug ConcurrentModificationException effectively
  3. Find and fix collection-related memory leaks
  4. Test thread-safety of concurrent collections
  5. 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)
Code
Loading syntax highlighter...

The Problem

TEXT(21 lines)
Code
Loading syntax highlighter...

The Better Test

JAVA(32 lines)
Code
Loading syntax highlighter...

Mental Model: Test Coverage Pyramid for Collections

TEXT(34 lines)
Code
Loading syntax highlighter...

Deep Dive: AssertJ Collection Assertions

Basic Assertions

JAVA(14 lines)
Code
Loading syntax highlighter...

Content Assertions

JAVA(21 lines)
Code
Loading syntax highlighter...

Condition-Based Assertions

JAVA(19 lines)
Code
Loading syntax highlighter...

Extracting Properties

JAVA(25 lines)
Code
Loading syntax highlighter...

Filtering

JAVA(18 lines)
Code
Loading syntax highlighter...

Map Assertions

JAVA(28 lines)
Code
Loading syntax highlighter...

Deep Dive: Hamcrest Matchers

Collection Matchers

JAVA(19 lines)
Code
Loading syntax highlighter...

Map Matchers

JAVA(6 lines)
Code
Loading syntax highlighter...

Combining Matchers

JAVA(16 lines)
Code
Loading syntax highlighter...

Deep Dive: Debugging Collection Issues

ConcurrentModificationException

JAVA(38 lines)
Code
Loading syntax highlighter...

Memory Leak Detection

JAVA(44 lines)
Code
Loading syntax highlighter...

Debugging with IntelliJ

JAVA(27 lines)
Code
Loading syntax highlighter...

Deep Dive: Testing Thread Safety

Basic Concurrent Test

JAVA(21 lines)
Code
Loading syntax highlighter...

Testing for Race Conditions

JAVA(38 lines)
Code
Loading syntax highlighter...

Using JCStress (for serious concurrency testing)

JAVA(22 lines)
Code
Loading syntax highlighter...

Deep Dive: Testing Edge Cases

Null Handling

JAVA(25 lines)
Code
Loading syntax highlighter...

Empty Collection Handling

JAVA(17 lines)
Code
Loading syntax highlighter...

Boundary Conditions

JAVA(33 lines)
Code
Loading syntax highlighter...

Order Verification

JAVA(35 lines)
Code
Loading syntax highlighter...

⚠️ Common Mistakes

Mistake 1: Not Testing Exact Content

JAVA(15 lines)
Code
Loading syntax highlighter...

Mistake 2: Order Sensitivity in Set Tests

JAVA(14 lines)
Code
Loading syntax highlighter...

Mistake 3: Shallow Collection Comparison

JAVA(17 lines)
Code
Loading syntax highlighter...

Mistake 4: Not Testing Immutability

JAVA(17 lines)
Code
Loading syntax highlighter...

Mistake 5: Flaky Concurrent Tests

JAVA(23 lines)
Code
Loading syntax highlighter...

πŸ’» Exercises

Exercise 1: Collection Test Suite

Write comprehensive tests for this method:

JAVA(5 lines)
Code
Loading syntax highlighter...
βœ… Solution:
JAVA(55 lines)
Code
Loading syntax highlighter...

Exercise 2: Debug the Race Condition

Fix this thread-unsafe code and write a test that catches the bug:

JAVA(12 lines)
Code
Loading syntax highlighter...
βœ… Solution:
JAVA(40 lines)
Code
Loading syntax highlighter...

Exercise 3: Memory Leak Test

Write a test that detects if a cache is leaking memory:

JAVA(19 lines)
Code
Loading syntax highlighter...
βœ… Solution:
JAVA(52 lines)
Code
Loading 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)
Code
Loading syntax highlighter...

Question 2: Testing Thread Safety

Q: How do you test that a collection implementation is thread-safe?
A:
  1. Multiple threads hitting same operation:
JAVA(14 lines)
Code
Loading syntax highlighter...
  1. Race condition tests with CountDownLatch:
JAVA(2 lines)
Code
Loading syntax highlighter...
  1. JCStress for thorough analysis
  2. @RepeatedTest to catch intermittent failures

Question 3: Debugging ConcurrentModificationException

Q: How do you debug a ConcurrentModificationException that only happens in production?
A:
  1. Add logging around collection operations:
JAVA(5 lines)
Code
Loading syntax highlighter...
  1. Check for multi-threaded access:
JAVA(3 lines)
Code
Loading syntax highlighter...
  1. Use stack traces from the exception:
  • The exception includes both the iterator creation and modification point
  • Compare thread IDs in logs around those times
  1. 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:
  1. Heap dump analysis:
BASH(2 lines)
Code
Loading syntax highlighter...
  1. JFR recording:
BASH
Code
Loading syntax highlighter...
  1. Test with assertions:
JAVA(7 lines)
Code
Loading syntax highlighter...
  1. WeakHashMap test:
JAVA(5 lines)
Code
Loading syntax highlighter...

Question 5: Test Isolation

Q: How do you ensure tests don't affect each other when testing shared collections?
A:
  1. Create fresh instances:
JAVA(4 lines)
Code
Loading syntax highlighter...
  1. Use defensive copies:
JAVA(7 lines)
Code
Loading syntax highlighter...
  1. Avoid static shared state
  2. Clear collections in @AfterEach if reusing
  3. Use @DirtiesContext in Spring if needed

πŸ“ Summary & Key Takeaways

AssertJ Essentials

AssertionUse 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

  1. Size: Empty, one, many, boundary
  2. Content: Expected, unexpected, duplicates, nulls
  3. Order: Insertion, sorted, reversed
  4. Mutation: Add, remove, update, clear
  5. Thread safety: Concurrent operations
  6. 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:

  1. Use assertThat(list).containsExactly() for precise verification
  2. Test edge cases: empty, single, large, with nulls
  3. Verify immutability by attempting modification
  4. Use proper synchronization in concurrent tests
  5. 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