如何在集成测试中Mock被Castle Windsor装饰的数据源类
Great question! This is a common pain point when working with Castle Windsor's decorator chains in integration tests—you want to reuse your production installer logic but swap out a lower-level dependency without breaking the entire chain. Here's a solid solution that keeps your production code intact and validates the real decorator flow:
Core Approach
We'll leverage Castle Windsor's component registration priority to:
- First register all your production components (including the full decorator chain) using your existing
IWindsorInstallerimplementation. - Then override only the lowest-level
IDataSourceregistration with your mock, ensuring the rest of the decorator chain (Client → Cache → Computation) remains exactly as it is in production.
Step-by-Step Implementation
1. Initialize Container & Run Production Installer
Start by setting up your Windsor container and running your production installer—this ensures all your decorator components are registered exactly as they are in production, no code changes needed:
var container = new WindsorContainer(); // Reuse your production installer to register the full decorator chain container.Install(new ProductionInstaller());
2. Register Mock DataSource as Default Component
Next, register your mock data source and mark it as the default implementation for IDataSource. This will override the production DataSource registration without touching the rest of the decorator chain:
// Create your mock (use a custom mock class or Moq, whichever fits your setup) var mockedDataSource = new MockedData(); // Or with Moq: var mockedDataSource = new Mock<IDataSource>().Object; // Register the mock and set it as the default IDataSource implementation container.Register( Component.For<IDataSource>() .Instance(mockedDataSource) .IsDefault() .Named("MockedDataSource") );
3. Verify the Decorator Chain
When you resolve IClient from the container now, it will automatically build the chain:Client → Cache → Computation → MockedData
This works because each upper-level component (Cache, Computation) depends on the interface IComputation/IDataSource, not the concrete production implementation. Windsor will inject the default IDataSource (your mock) into Computation, keeping the rest of the decorator logic identical to production.
Handling Named Component Registrations (If Applicable)
If your production installer uses named registrations for components (e.g., naming the real data source "RealDataSource"), you'll need to adjust the dependency for Computation to point to your mock instead:
// Register the mock with a name container.Register(Component.For<IDataSource>().Instance(mockedDataSource).Named("MockedDataSource")); // Update the Computation component's dependency to use the mock container.Kernel.ComponentRegistered += (key, handler) => { if (key.Service == typeof(IComputation)) { handler.ComponentModel.Dependencies.Clear(); handler.ComponentModel.Dependencies.Add(Dependency.OnComponent<IDataSource>("MockedDataSource")); } };
This is rare in most setups, but it's a handy fallback if your production code uses explicit named dependencies.
Why This Works
- No production code changes: You don't need to split your
Installmethod or adjust registration order, eliminating the risk of introducing production bugs. - Validates real decorator logic: Your test uses the exact same decorator chain as production—only the data source is swapped out, so you're validating that the decorators behave correctly in a real scenario.
- Leverages Windsor's native features: The
IsDefault()flag and interface-based dependency injection are core Windsor features, so this approach is stable and maintainable.
内容的提问来源于stack exchange,提问作者Al Kepp

