-
-
Notifications
You must be signed in to change notification settings - Fork 301
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add new Testcontainers.Xunit project
- Loading branch information
Showing
13 changed files
with
441 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
root = true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
namespace DotNet.Testcontainers.Xunit; | ||
|
||
/// <summary> | ||
/// Fixture for sharing a container instance across multiple tests in a single class. | ||
/// See <a href="https://xunit.net/docs/shared-context">Shared Context between Tests</a> from xUnit.net documentation for more information about fixtures. | ||
/// A logger is automatically configured to write diagnostic messages to xUnit's <see cref="IMessageSink"/>. | ||
/// </summary> | ||
/// <param name="messageSink">The message sink used for reporting diagnostic messages.</param> | ||
/// <typeparam name="TBuilderEntity">The builder entity.</typeparam> | ||
/// <typeparam name="TContainerEntity">The container entity.</typeparam> | ||
[PublicAPI] | ||
public class ContainerFixture<TBuilderEntity, TContainerEntity>(IMessageSink messageSink) : IAsyncLifetime | ||
where TBuilderEntity : IContainerBuilder<TBuilderEntity, TContainerEntity>, new() | ||
where TContainerEntity : IContainer | ||
{ | ||
private readonly CancellationTokenSource _cts = new CancellationTokenSource(); | ||
private TContainerEntity _container; | ||
|
||
/// <summary> | ||
/// The message sink used for reporting diagnostic messages. | ||
/// </summary> | ||
protected IMessageSink MessageSink { get; } = messageSink; | ||
|
||
/// <summary> | ||
/// The container instance. | ||
/// </summary> | ||
public TContainerEntity Container | ||
{ | ||
get | ||
{ | ||
if (_container == null) | ||
{ | ||
var containerBuilder = new TBuilderEntity().WithLogger(new MessageSinkLogger(MessageSink)); | ||
_container = Configure(containerBuilder).Build(); | ||
} | ||
return _container; | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Extension point to further configure the container instance. | ||
/// </summary> | ||
/// <example> | ||
/// <code> | ||
/// public class MariaDbRootUserFixture(IMessageSink messageSink) : DbContainerFixture<MariaDbBuilder, MariaDbContainer>(messageSink) | ||
/// { | ||
/// public override DbProviderFactory DbProviderFactory => MySqlConnectorFactory.Instance; | ||
/// | ||
/// protected override MariaDbBuilder Configure(MariaDbBuilder builder) | ||
/// { | ||
/// return builder.WithUsername("root"); | ||
/// } | ||
/// } | ||
/// </code> | ||
/// </example> | ||
/// <param name="builder">The container builder.</param> | ||
/// <returns>A configured instance of <typeparamref name="TBuilderEntity" />.</returns> | ||
protected virtual TBuilderEntity Configure(TBuilderEntity builder) | ||
{ | ||
return builder; | ||
} | ||
|
||
/// <summary> | ||
/// The maximum allowed time for the container to start before a timeout occurs. | ||
/// Defaults to 15 minutes. | ||
/// </summary> | ||
protected virtual TimeSpan StartTimeout { get; } = TimeSpan.FromMinutes(15); | ||
|
||
/// <inheritdoc /> | ||
public virtual Task InitializeAsync() | ||
{ | ||
_cts.CancelAfter(StartTimeout); | ||
return Container.StartAsync(_cts.Token); | ||
} | ||
|
||
/// <inheritdoc /> | ||
public virtual Task DisposeAsync() | ||
{ | ||
_cts.Dispose(); | ||
return Container.DisposeAsync().AsTask(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
namespace DotNet.Testcontainers.Xunit; | ||
|
||
/// <summary> | ||
/// Base class for tests needing a container per test method. | ||
/// A logger is automatically configured to write messages to xUnit's <see cref="ITestOutputHelper" />. | ||
/// </summary> | ||
/// <typeparam name="TBuilderEntity">The builder entity.</typeparam> | ||
/// <typeparam name="TContainerEntity">The container entity.</typeparam> | ||
[PublicAPI] | ||
public abstract class ContainerTest<TBuilderEntity, TContainerEntity> : IAsyncLifetime | ||
where TBuilderEntity : IContainerBuilder<TBuilderEntity, TContainerEntity>, new() | ||
where TContainerEntity : IContainer | ||
{ | ||
private readonly CancellationTokenSource _cts = new CancellationTokenSource(); | ||
|
||
protected ContainerTest(ITestOutputHelper testOutputHelper, Func<TBuilderEntity, TBuilderEntity> configure = null) | ||
{ | ||
var builder = new TBuilderEntity().WithLogger(new TestOutputLogger(testOutputHelper)); | ||
Container = configure == null ? builder.Build() : configure(builder).Build(); | ||
} | ||
|
||
/// <summary> | ||
/// The container instance. | ||
/// </summary> | ||
protected TContainerEntity Container { get; } | ||
|
||
/// <summary> | ||
/// The maximum allowed time for the container to start before a timeout occurs. | ||
/// Defaults to 15 minutes. | ||
/// </summary> | ||
protected virtual TimeSpan StartTimeout { get; } = TimeSpan.FromMinutes(15); | ||
|
||
/// <inheritdoc /> | ||
public virtual Task InitializeAsync() | ||
{ | ||
_cts.CancelAfter(StartTimeout); | ||
return Container.StartAsync(_cts.Token); | ||
} | ||
|
||
/// <inheritdoc /> | ||
public virtual Task DisposeAsync() | ||
{ | ||
_cts.Dispose(); | ||
return Container.DisposeAsync().AsTask(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
namespace DotNet.Testcontainers.Xunit; | ||
|
||
/// <summary> | ||
/// Fixture for sharing a database container instance across multiple tests in a single class. | ||
/// See <a href="https://xunit.net/docs/shared-context">Shared Context between Tests</a> from xUnit.net documentation for more information about fixtures. | ||
/// A logger is automatically configured to write diagnostic messages to xUnit's <see cref="IMessageSink"/>. | ||
/// </summary> | ||
/// <typeparam name="TBuilderEntity">The builder entity.</typeparam> | ||
/// <typeparam name="TContainerEntity">The container entity.</typeparam> | ||
[PublicAPI] | ||
public abstract class DbContainerFixture<TBuilderEntity, TContainerEntity>(IMessageSink messageSink) : ContainerFixture<TBuilderEntity, TContainerEntity>(messageSink), IDbContainerTestMethods | ||
where TBuilderEntity : IContainerBuilder<TBuilderEntity, TContainerEntity>, new() | ||
where TContainerEntity : IContainer, IDatabaseContainer | ||
{ | ||
private DbContainerTestMethods _testMethods; | ||
|
||
/// <inheritdoc /> | ||
public override async Task InitializeAsync() | ||
{ | ||
await base.InitializeAsync(); | ||
_testMethods = new DbContainerTestMethods(DbProviderFactory, ConnectionString); | ||
} | ||
|
||
/// <inheritdoc /> | ||
public override async Task DisposeAsync() | ||
{ | ||
if (_testMethods != null) | ||
{ | ||
await _testMethods.DisposeAsync() | ||
.ConfigureAwait(true); | ||
} | ||
|
||
await base.DisposeAsync() | ||
.ConfigureAwait(true); | ||
} | ||
|
||
/// <summary> | ||
/// The <see cref="DbProviderFactory"/> used to create <see cref="DbConnection"/> instances. | ||
/// </summary> | ||
public abstract DbProviderFactory DbProviderFactory { get; } | ||
|
||
/// <summary> | ||
/// Gets the database connection string. | ||
/// </summary> | ||
public virtual string ConnectionString => Container.GetConnectionString(); | ||
|
||
/// <inheritdoc /> | ||
public DbConnection CreateConnection() => _testMethods.CreateConnection(); | ||
|
||
#if NET8_0_OR_GREATER | ||
/// <inheritdoc /> | ||
public DbConnection OpenConnection() => _testMethods.OpenConnection(); | ||
|
||
/// <inheritdoc /> | ||
public ValueTask<DbConnection> OpenConnectionAsync(CancellationToken cancellationToken = default) => _testMethods.OpenConnectionAsync(cancellationToken); | ||
|
||
/// <inheritdoc /> | ||
public DbCommand CreateCommand(string commandText = null) => _testMethods.CreateCommand(commandText); | ||
|
||
/// <inheritdoc /> | ||
public DbBatch CreateBatch() => _testMethods.CreateBatch(); | ||
#endif | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
namespace DotNet.Testcontainers.Xunit; | ||
|
||
/// <summary> | ||
/// Base class for tests needing a database container per test method. | ||
/// A logger is automatically configured to write messages to xUnit's <see cref="ITestOutputHelper"/>. | ||
/// </summary> | ||
/// <typeparam name="TBuilderEntity">The builder entity.</typeparam> | ||
/// <typeparam name="TContainerEntity">The container entity.</typeparam> | ||
[PublicAPI] | ||
public abstract class DbContainerTest<TBuilderEntity, TContainerEntity> : ContainerTest<TBuilderEntity, TContainerEntity>, IDbContainerTestMethods | ||
where TBuilderEntity : IContainerBuilder<TBuilderEntity, TContainerEntity>, new() | ||
where TContainerEntity : IContainer, IDatabaseContainer | ||
{ | ||
private readonly CancellationTokenSource _cts = new CancellationTokenSource(); | ||
private DbContainerTestMethods _testMethods; | ||
|
||
protected DbContainerTest(ITestOutputHelper testOutputHelper, Func<TBuilderEntity, TBuilderEntity> configure = null) | ||
: base(testOutputHelper, configure) | ||
{ | ||
} | ||
|
||
/// <inheritdoc /> | ||
public override async Task InitializeAsync() | ||
{ | ||
await base.InitializeAsync(); | ||
_testMethods = new DbContainerTestMethods(DbProviderFactory, ConnectionString); | ||
} | ||
|
||
/// <inheritdoc /> | ||
public override async Task DisposeAsync() | ||
{ | ||
if (_testMethods != null) | ||
{ | ||
await _testMethods.DisposeAsync() | ||
.ConfigureAwait(true); | ||
} | ||
|
||
await base.DisposeAsync() | ||
.ConfigureAwait(true); | ||
} | ||
|
||
/// <summary> | ||
/// The <see cref="DbProviderFactory"/> used to create <see cref="DbConnection"/> instances. | ||
/// </summary> | ||
public abstract DbProviderFactory DbProviderFactory { get; } | ||
|
||
/// <summary> | ||
/// Gets the database connection string. | ||
/// </summary> | ||
public virtual string ConnectionString => Container.GetConnectionString(); | ||
|
||
/// <inheritdoc /> | ||
public DbConnection CreateConnection() => _testMethods.CreateConnection(); | ||
|
||
#if NET8_0_OR_GREATER | ||
/// <inheritdoc /> | ||
public DbConnection OpenConnection() => _testMethods.OpenConnection(); | ||
|
||
/// <inheritdoc /> | ||
public ValueTask<DbConnection> OpenConnectionAsync(CancellationToken cancellationToken = default) => _testMethods.OpenConnectionAsync(cancellationToken); | ||
|
||
/// <inheritdoc /> | ||
public DbCommand CreateCommand(string commandText = null) => _testMethods.CreateCommand(commandText); | ||
|
||
/// <inheritdoc /> | ||
public DbBatch CreateBatch() => _testMethods.CreateBatch(); | ||
#endif | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
namespace DotNet.Testcontainers.Xunit; | ||
|
||
internal class DbContainerTestMethods(DbProviderFactory dbProviderFactory, string connectionString) : IDbContainerTestMethods, IAsyncDisposable | ||
{ | ||
private readonly DbProviderFactory _dbProviderFactory = dbProviderFactory ?? throw new ArgumentNullException(nameof(dbProviderFactory)); | ||
private readonly string _connectionString = connectionString ?? throw new ArgumentNullException(nameof(connectionString)); | ||
|
||
#if NET8_0_OR_GREATER | ||
[CanBeNull] | ||
private DbDataSource _dbDataSource; | ||
private DbDataSource DbDataSource | ||
{ | ||
get | ||
{ | ||
_dbDataSource ??= _dbProviderFactory.CreateDataSource(_connectionString); | ||
return _dbDataSource; | ||
} | ||
} | ||
|
||
public DbConnection CreateConnection() => DbDataSource.CreateConnection(); | ||
|
||
public DbConnection OpenConnection() => DbDataSource.OpenConnection(); | ||
|
||
public ValueTask<DbConnection> OpenConnectionAsync(CancellationToken cancellationToken = default) => DbDataSource.OpenConnectionAsync(cancellationToken); | ||
|
||
public DbCommand CreateCommand(string commandText = null) => DbDataSource.CreateCommand(commandText); | ||
|
||
public DbBatch CreateBatch() => DbDataSource.CreateBatch(); | ||
|
||
public ValueTask DisposeAsync() => _dbDataSource?.DisposeAsync() ?? ValueTask.CompletedTask; | ||
#else | ||
public DbConnection CreateConnection() | ||
{ | ||
var connection = _dbProviderFactory.CreateConnection() ?? throw new InvalidOperationException($"DbProviderFactory.CreateConnection() returned null for {_dbProviderFactory}"); | ||
connection.ConnectionString = _connectionString; | ||
return connection; | ||
} | ||
|
||
public ValueTask DisposeAsync() => default; | ||
#endif | ||
} |
Oops, something went wrong.