State vs Interaction Testing
Fast recall
Quick revision
Core answer
Interview-ready guide
Interview answer
State-based testing is when you assert on the result or final state of an object.
You call a method and check what it returned or how the object changed.
For example, testing a calculator—you call calculate total and assert the result is one-twenty. You don't care how it computed the value, just that it's correct. That's state-based.
Interaction-based testing is when you verify that the right methods were called on collaborators. You use mocks and write verify statements.
For example, testing that a user registration sends a welcome email. You verify the email service's send method was called. You're checking the collaboration, not the return value.
Mocking lives in the interaction world. The key difference is what you're verifying. ' State tests are better for refactoring because you can change the internal implementation and the test still passes.
Interaction tests are fragile—if you refactor the calls, the test breaks even if behavior is unchanged. The guideline is: default to state-based. Use interaction-based only for side effects at system boundaries—external APIs, databases, messaging.
Don't verify internal calls. Freshers often over-verify every method call, which couples the test to implementation details. If you refactor a private helper, the test shouldn't break.
That's why interaction tests should be reserved for things like verifying an audit log was written or a message was published—side effects that don't return useful values.
State vs Interaction Testing
Direct Answer
State-based testing asserts on the final state of an object; interaction-based testing, where mocking lives, asserts that the right collaborators were called in the right way.
Code Example
// STATE-BASED: assert the result
double total = orderService.calculateTotal(order);
assertEquals(120.0, total);
// INTERACTION-BASED: assert the collaboration (mocking lives here)
userService.register(newUser);
verify(emailService).sendWelcome(newUser.email());
Common Mistakes & Pitfalls
- Over-verifying internal calls: Freshers verify every method call on every dependency, including internal helpers that are implementation details. This makes tests brittle—refactoring a private method breaks tests even though public behavior is unchanged.
- Not verifying side effects: When testing a method that should send a notification or log an audit entry, beginners only assert the return value and forget to verify the side effect happened.
- Mixing styles inconsistently: Some tests assert on state and verify interactions for the same operation, creating redundant assertions that obscure what's actually being tested. Pick one style per test case—either assert the outcome or verify the collaboration, not both unless necessary.
Check your understanding
Answer these without looking back at the article.
- Can the same test use both state-based and interaction-based assertions?
- Why are interaction-based tests considered brittle?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.