TDD实践疑问:编写测试前需明确依赖吗?多实现需重写测试?
Let’s break down your three key questions with your code context in mind, keeping it grounded in real-world TDD practice:
1. Is adjusting tests to add dependencies mid-implementation normal in TDD?
Absolutely—this is totally standard and even a core part of how TDD drives better design. TDD is iterative, not a "design everything upfront" process.
When you first wrote your test, you focused on the core behavior you cared about: "When I call DoSomething(), it should return true". You didn’t need to think about dependencies because you hadn’t explored how the service would actually achieve that behavior yet. As you started writing the implementation, you realized you needed IDependency to get the job done—so you updated the service’s constructor, then adjusted the test to provide a fake/mock dependency to keep it compiling and failing (until you get the implementation right).
This is exactly the "red-green-refactor" cycle at work: you start with a failing test for the behavior, then iterate on both the implementation and test as you uncover more about what the system needs.
2. Do I need to identify all dependencies before writing tests?
Nope—you don’t, and you shouldn’t try to. TDD is all about driving design from behavior, not upfront architecture.
Your initial test focused on what the service does, not how it does it. By delaying decisions about dependencies until you actually need them, you avoid over-engineering your code with unnecessary abstractions or dependencies that might not even be required in the end. Waiting until implementation to discover dependencies helps you build a more focused, minimal design—you only add what’s necessary to make the current failing test pass.
3. Do I need to write new tests for every new implementation of IService with different dependencies?
It depends on what you’re testing:
If the new implementation must adhere to the same
IServicebehavior contract: You don’t need new tests for the core behavior. Instead, reuse your existing test (or refactor it into an interface-focused test suite) and pass in the appropriate dependencies (real or mocked) for the new implementation. The test should verify that anyIServiceimplementation meets the behavior requirement (e.g.,DoSomething()returnstrue), regardless of its internal dependencies.If the new implementation has unique behavior or edge cases: You’ll need to add tests specific to those scenarios. For example, if a new
Servicevariant returnsfalsewhenIDependencyreturns a certain value, you’d write a test for that edge case using a mockIDependencythat simulates that scenario.If you need to test interactions with dependencies: You might write separate tests to verify that the service correctly uses its dependencies (e.g., "Does the service call
IDependency.DoThatThing()whenDoSomething()is invoked?"). These are interaction tests, not behavior tests, and they’re specific to how the implementation works—so you’d write them per implementation if needed.
Example of reusing interface tests
Instead of a test tied to Service, refactor to test the IService contract directly:
[TestMethod] public void IService_DoSomething_ReturnsTrue(IService service) { bool result = service.DoSomething(); Assert.IsTrue(result); } // Run this test with different implementations: // - new Service(new MockDependency()) // - new AnotherService(new RealDependency()) // - new MockService()
This way, you validate that all implementations meet the core behavior without duplicating test code.
内容的提问来源于stack exchange,提问作者zola25

