22
33namespace Xunit . Microsoft . DependencyInjection . Abstracts ;
44
5+ /// <summary>
6+ /// Base fixture abstraction that configures dependency injection, configuration sources
7+ /// (JSON, user secrets, environment variables) and logging for test classes.
8+ /// Derived fixtures register services via <see cref="AddServices"/> and configuration files
9+ /// via <see cref="GetTestAppSettings"/>.
10+ /// </summary>
511public abstract class TestBedFixture : IDisposable , IAsyncDisposable
612{
713 private readonly ServiceCollection _services ;
@@ -10,6 +16,9 @@ public abstract class TestBedFixture : IDisposable, IAsyncDisposable
1016 private bool _disposedAsync ;
1117 private bool _servicesAdded ;
1218
19+ /// <summary>
20+ /// Initializes the fixture, creating a service collection and configuration builder.
21+ /// </summary>
1322 protected TestBedFixture ( )
1423 {
1524 _services = new ServiceCollection ( ) ;
@@ -19,9 +28,21 @@ protected TestBedFixture()
1928 _servicesAdded = false ;
2029 }
2130
31+ /// <summary>
32+ /// The combined configuration root composed of JSON files, user secrets (optional) and environment variables.
33+ /// </summary>
2234 public IConfigurationRoot ? Configuration { get ; private set ; }
35+
36+ /// <summary>
37+ /// The configuration builder used to assemble the <see cref="Configuration"/>.
38+ /// </summary>
2339 public IConfigurationBuilder ConfigurationBuilder { get ; private set ; }
2440
41+ /// <summary>
42+ /// Builds (lazily) and returns the root <see cref="ServiceProvider"/> including logging provider and options.
43+ /// Subsequent calls return a cached provider.
44+ /// </summary>
45+ /// <param name="testOutputHelper">The test output helper used for logging.</param>
2546 public ServiceProvider GetServiceProvider ( ITestOutputHelper testOutputHelper )
2647 {
2748 if ( _serviceProvider is not null )
@@ -38,22 +59,37 @@ public ServiceProvider GetServiceProvider(ITestOutputHelper testOutputHelper)
3859 return _serviceProvider = _services . BuildServiceProvider ( ) ;
3960 }
4061
62+ /// <summary>
63+ /// Resolves a scoped service of type <typeparamref name="T"/> using a new scope.
64+ /// </summary>
4165 public T ? GetScopedService < T > ( ITestOutputHelper testOutputHelper )
4266 {
4367 var serviceProvider = GetServiceProvider ( testOutputHelper ) ;
4468 using var scope = serviceProvider . CreateScope ( ) ;
4569 return scope . ServiceProvider . GetService < T > ( ) ;
4670 }
4771
72+ /// <summary>
73+ /// Creates and returns a new asynchronous service scope.
74+ /// Caller is responsible for disposing the returned <see cref="AsyncServiceScope"/>.
75+ /// </summary>
4876 public AsyncServiceScope GetAsyncScope ( ITestOutputHelper testOutputHelper )
4977 {
5078 var serviceProvider = GetServiceProvider ( testOutputHelper ) ;
5179 return serviceProvider . CreateAsyncScope ( ) ;
5280 }
5381
82+ /// <summary>
83+ /// Resolves a service of type <typeparamref name="T"/> from the root provider.
84+ /// </summary>
5485 public T ? GetService < T > ( ITestOutputHelper testOutputHelper )
5586 => GetServiceProvider ( testOutputHelper ) . GetService < T > ( ) ;
5687
88+ /// <summary>
89+ /// Resolves a keyed service of type <typeparamref name="T"/>.
90+ /// </summary>
91+ /// <param name="key">The key identifying the registration.</param>
92+ /// <param name="testOutputHelper">The test output helper used for logging and provider access.</param>
5793 public T ? GetKeyedService < T > ( [ DisallowNull ] string key , ITestOutputHelper testOutputHelper )
5894 => GetServiceProvider ( testOutputHelper ) . GetKeyedService < T > ( key ) ;
5995
@@ -64,13 +100,15 @@ public AsyncServiceScope GetAsyncScope(ITestOutputHelper testOutputHelper)
64100 // Dispose(disposing: false);
65101 // }
66102
103+ /// <inheritdoc />
67104 public void Dispose ( )
68105 {
69106 // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
70107 Dispose ( disposing : true ) ;
71108 GC . SuppressFinalize ( this ) ;
72109 }
73110
111+ /// <inheritdoc />
74112 public async ValueTask DisposeAsync ( )
75113 {
76114 if ( ! _disposedAsync )
@@ -82,13 +120,31 @@ public async ValueTask DisposeAsync()
82120 GC . SuppressFinalize ( this ) ;
83121 }
84122
123+ /// <summary>
124+ /// Adds services to the service collection. Called once before building the provider.
125+ /// </summary>
85126 protected abstract void AddServices ( IServiceCollection services , IConfiguration ? configuration ) ;
127+
128+ /// <summary>
129+ /// Returns the test application settings descriptors (JSON files) to include.
130+ /// </summary>
86131 protected abstract IEnumerable < TestAppSettings > GetTestAppSettings ( ) ;
132+
133+ /// <summary>
134+ /// Override to asynchronously clean up resources created by the fixture.
135+ /// </summary>
87136 protected abstract ValueTask DisposeAsyncCore ( ) ;
88137
138+ /// <summary>
139+ /// Allows derived fixtures to customize logging by adding or decorating providers.
140+ /// </summary>
89141 protected virtual ILoggingBuilder AddLoggingProvider ( ILoggingBuilder loggingBuilder , ILoggerProvider loggerProvider )
90142 => loggingBuilder . AddProvider ( loggerProvider ) ;
91143
144+ /// <summary>
145+ /// Override to add user secrets to the provided configuration builder when needed.
146+ /// Default implementation does nothing.
147+ /// </summary>
92148 protected virtual void AddUserSecrets ( IConfigurationBuilder configurationBuilder ) { }
93149
94150 private IConfigurationRoot ? GetConfigurationRoot ( )
@@ -110,6 +166,9 @@ private IConfigurationRoot GetConfigurationRoot(IEnumerable<TestAppSettings> con
110166 return ConfigurationBuilder . Build ( ) ;
111167 }
112168
169+ /// <summary>
170+ /// Disposes managed resources created by the fixture including the root service provider.
171+ /// </summary>
113172 protected virtual void Dispose ( bool disposing )
114173 {
115174 if ( ! _disposedValue )
@@ -124,8 +183,6 @@ protected virtual void Dispose(bool disposing)
124183 _services . Clear ( ) ;
125184 }
126185
127- // TODO: free unmanaged resources (unmanaged objects) and override finalizer
128- // TODO: set large fields to null
129186 _disposedValue = true ;
130187 }
131188 }
0 commit comments