使用Mockito.verifyNoMoreInteractions()强制测试更新的有效性探讨
Oh, this is such a smart use of Mockito.verifyNoMoreInteractions() to keep tests aligned with code changes—let me break down exactly how this works with your example scenario.
verifyNoMoreInteractions() Acts as a Test Sync Guard The core idea here is to prevent "stale" tests that pass even after the underlying code logic has changed. When you use this method after verifying all expected interactions with your mocks, Mockito will throw an error if any unapproved calls to your dependencies occur. This forces anyone modifying the testedMethod to update the test case to match the new behavior, ensuring your tests always reflect the actual code.
First, let's flesh out your TestedClass with realistic dependency calls (since you mentioned there are 10+ in practice):
class TestedClass { DependencyA dependency1; DependencyB dependency2; DependencyC dependency3; public void testedMethod(SomeClass a) { if(a == null) { return; } // Real-world logic with multiple dependency calls dependency1.process(a); dependency2.validate(a); dependency3.store(a); // ... more similar calls to other dependencies } }
Now here's how the JUnit test would use verifyNoMoreInteractions() to enforce test sync:
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) class TestedClassTest { @Mock private DependencyA dependency1; @Mock private DependencyB dependency2; @Mock private DependencyC dependency3; @InjectMocks private TestedClass testedClass; @Test void testedMethod_WithNonNullArgument_ShouldExecuteAllExpectedDependencyCalls() { // Arrange SomeClass testInput = new SomeClass(); // Act testedClass.testedMethod(testInput); // Assert: Verify all expected interactions first verify(dependency1).process(testInput); verify(dependency2).validate(testInput); verify(dependency3).store(testInput); // Add the guard check for unexpected interactions verifyNoMoreInteractions(dependency1, dependency2, dependency3); } @Test void testedMethod_WithNullArgument_ShouldNotCallAnyDependencies() { // Act testedClass.testedMethod(null); // For this case, verifyNoInteractions is more explicit, but verifyNoMoreInteractions would also work verifyNoInteractions(dependency1, dependency2, dependency3); } }
Let's walk through two common future change scenarios:
- New dependency call added: Suppose someone updates
testedMethodto includedependency4.log(a). If they forget to addverify(dependency4).log(testInput)to the test,verifyNoMoreInteractions()will fail immediately, alerting them that an unapproved interaction occurred. They'll have to update the test to include this new verification. - Existing dependency call removed: If someone deletes
dependency2.validate(a)fromtestedMethodbut leavesverify(dependency2).validate(testInput)in the test, the initialverify()call will fail (since the interaction never happened). This tells the developer they need to remove that outdated verification from the test.
A quick note: This approach works best when the dependency interactions in testedMethod are consistent (no conditional, branch-dependent calls that might vary per test case). If you have logic where some calls are optional, verifyNoMoreInteractions() can make tests overly fragile—but in your colleague's case (10+ fixed calls), it's perfect for keeping tests in lockstep with code.
内容的提问来源于stack exchange,提问作者Piotr Śmietana

