Resistance to refactoring measures whether a test continues to pass when application code is restructured without changing its behavior. A resistant test checks the result visible to users or collaborating systems; a brittle test checks the internal steps used to produce it. The metric therefore targets false positives: failures that report a problem even though the functionality still works. section 4.1 section 4.6
Assess resistance to refactoring by applying these practical rules:
- Verify the end result produced by the system under test rather than its algorithm, internal structure, source code, call sequence, or selected collaborators. section 4.1 section 4.6
- Treat a test that fails after behavior-preserving restructuring as a false-positive generator; repeated false alarms erode attention to failures and trust in the test suite. section 4.2 section 4.6
- Prefer black-box coupling to observable behavior when possible; white-box tests can protect against regressions more strongly but sacrifice refactoring resistance. section 4.5
- Maximize resistance to refactoring and maintainability, then balance regression protection against fast feedback; no test can maximize every attribute simultaneously. section 4.4
- Mock or verify interactions at system edges rather than through intermediate application abstractions, which preserves behavior-focused tests while retaining integration coverage. section 9.1
Testing rendered output instead of structure
A structural test for MessageRenderer asserts that SubRenderers contains exactly three concrete renderer types in a fixed order. Refactoring that composition would fail the test even if rendering remains correct. A behavior-focused test supplies a message and asserts the resulting HTML, <h1>h</h1><b>b</b><i>f</i>. The latter couples to observable output and should survive changes to the rendering algorithm. section 4.1
Listing 4.4. Verifying the outcome that MessageRenderer produces
[Fact]
public void Rendering_a_message()
{
var sut = new MessageRenderer();
var message = new Message
{
Header = "h",
Body = "b",
Footer = "f"
};
string html = sut.Render(message);
Assert.Equal("<h1>h</h1><b>b</b><i>f</i>", html);
}
Inspect the concrete MessageRenderer test that asserts the HTML result rather than the renderer composition. section 4.1
Figure 4.2. The test on the left couples to the SUT’s observable behavior as opposed to implementation details. Such a test is resistant to refactoring—it will trigger few, if any, false positives.
Use the figure to contrast tests coupled to observable behavior with tests coupled to implementation details. section 4.1
