Skip to content

Commit

Permalink
Introduce a new IDatabaseContainer interface
Browse files Browse the repository at this point in the history
This is required in order to implement a generic database fixture that could look like this.

```csharp
using System;
using System.Data.Common;
using System.Threading.Tasks;
using DotNet.Testcontainers.Builders;
using DotNet.Testcontainers.Containers;
using Testcontainers.MsSql;
using Testcontainers.MySql;
using Testcontainers.PostgreSql;
using Xunit;

namespace SampleCode;

public abstract class DatabaseFixture<TBuilder, TContainer> : IAsyncLifetime
    where TBuilder : IContainerBuilder<TBuilder, TContainer>, new()
    where TContainer : IContainer, IDatabaseContainer
{
    private string _connectionString;
    private TContainer _container;

    protected abstract DbProviderFactory ProviderFactory { get; }

    public string ConnectionString => _connectionString ?? throw new InvalidOperationException($"{nameof(IAsyncLifetime.InitializeAsync)} must be called before accessing the connection string");

    async Task IAsyncLifetime.InitializeAsync()
    {
        _container = new TBuilder().Build();
        await _container.StartAsync();
        _connectionString = _container.GetConnectionString();
        using var connection = ProviderFactory.CreateConnection() ?? throw new InvalidOperationException($"ProviderFactory.CreateConnection() returned null ({ProviderFactory})");
        connection.ConnectionString = _connectionString;
        await connection.OpenAsync();
        await connection.CloseAsync();
    }

    async Task IAsyncLifetime.DisposeAsync()
    {
        if (_container != null)
        {
            await _container.StopAsync();
        }
    }
}

public class MsSqlDbFixture : DatabaseFixture<MsSqlBuilder, MsSqlContainer>
{
    protected override DbProviderFactory ProviderFactory => Microsoft.Data.SqlClient.SqlClientFactory.Instance;
}

public class MySqlDbFixture : DatabaseFixture<MySqlBuilder, MySqlContainer>
{
    protected override DbProviderFactory ProviderFactory => MySqlConnector.MySqlConnectorFactory.Instance;
}

public class PostgreSqlDbFixture : DatabaseFixture<PostgreSqlBuilder, PostgreSqlContainer>
{
    protected override DbProviderFactory ProviderFactory => Npgsql.NpgsqlFactory.Instance;
}

public class PostgreSqlTest : IClassFixture<PostgreSqlDbFixture>
{
    private readonly PostgreSqlDbFixture _dbFixture;

    public PostgreSqlTest(PostgreSqlDbFixture dbFixture) => _dbFixture = dbFixture;

    [Fact]
    public async Task TestOnPostgreSql()
    {
        await using var connection = new Npgsql.NpgsqlConnection(_dbFixture.ConnectionString);
        await using var command = connection.CreateCommand();

        // ...
    }
}
```

This will also be required to implement a new wait strategy for database where an ADO.NET provider is available.
  • Loading branch information
0xced committed Aug 31, 2023
1 parent 31e0b9b commit ebc03d7
Show file tree
Hide file tree
Showing 27 changed files with 95 additions and 21 deletions.
7 changes: 7 additions & 0 deletions Testcontainers.sln
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Tests", "tes
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.WebDriver.Tests", "tests\Testcontainers.WebDriver.Tests\Testcontainers.WebDriver.Tests.csproj", "{EBA72C3B-57D5-43FF-A5B4-3D55B3B6D4C2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Databases.Tests", "tests\Testcontainers.Databases.Tests\Testcontainers.Databases.Tests.csproj", "{DA54916E-1128-4200-B6AE-9F5BF02D832D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -394,6 +396,10 @@ Global
{EBA72C3B-57D5-43FF-A5B4-3D55B3B6D4C2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EBA72C3B-57D5-43FF-A5B4-3D55B3B6D4C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EBA72C3B-57D5-43FF-A5B4-3D55B3B6D4C2}.Release|Any CPU.Build.0 = Release|Any CPU
{DA54916E-1128-4200-B6AE-9F5BF02D832D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DA54916E-1128-4200-B6AE-9F5BF02D832D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DA54916E-1128-4200-B6AE-9F5BF02D832D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DA54916E-1128-4200-B6AE-9F5BF02D832D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{3F2E254F-C203-43FD-A078-DC3E2CBC0F9F} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
Expand Down Expand Up @@ -458,5 +464,6 @@ Global
{1A1983E6-5297-435F-B467-E8E1F11277D6} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{27CDB869-A150-4593-958F-6F26E5391E7C} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{EBA72C3B-57D5-43FF-A5B4-3D55B3B6D4C2} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{DA54916E-1128-4200-B6AE-9F5BF02D832D} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
EndGlobalSection
EndGlobal
2 changes: 1 addition & 1 deletion src/Testcontainers.Azurite/AzuriteContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ namespace Testcontainers.Azurite;

/// <inheritdoc cref="DockerContainer" />
[PublicAPI]
public sealed class AzuriteContainer : DockerContainer
public sealed class AzuriteContainer : DockerContainer, IDatabaseContainer
{
private const string AccountName = "devstoreaccount1";

Expand Down
2 changes: 1 addition & 1 deletion src/Testcontainers.CosmosDb/CosmosDbContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ namespace Testcontainers.CosmosDb;

/// <inheritdoc cref="DockerContainer" />
[PublicAPI]
public sealed class CosmosDbContainer : DockerContainer
public sealed class CosmosDbContainer : DockerContainer, IDatabaseContainer
{
/// <summary>
/// Initializes a new instance of the <see cref="CosmosDbContainer" /> class.
Expand Down
2 changes: 1 addition & 1 deletion src/Testcontainers.CouchDb/CouchDbContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ namespace Testcontainers.CouchDb;

/// <inheritdoc cref="DockerContainer" />
[PublicAPI]
public sealed class CouchDbContainer : DockerContainer
public sealed class CouchDbContainer : DockerContainer, IDatabaseContainer
{
private readonly CouchDbConfiguration _configuration;

Expand Down
2 changes: 1 addition & 1 deletion src/Testcontainers.Couchbase/CouchbaseContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ namespace Testcontainers.Couchbase;

/// <inheritdoc cref="DockerContainer" />
[PublicAPI]
public sealed class CouchbaseContainer : DockerContainer
public sealed class CouchbaseContainer : DockerContainer, IDatabaseContainer
{
private readonly CouchbaseConfiguration _configuration;

Expand Down
2 changes: 1 addition & 1 deletion src/Testcontainers.DynamoDb/DynamoDbContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ namespace Testcontainers.DynamoDb;

/// <inheritdoc cref="DockerContainer" />
[PublicAPI]
public sealed class DynamoDbContainer : DockerContainer
public sealed class DynamoDbContainer : DockerContainer, IDatabaseContainer
{
/// <summary>
/// Initializes a new instance of the <see cref="DynamoDbContainer" /> class.
Expand Down
2 changes: 1 addition & 1 deletion src/Testcontainers.Elasticsearch/ElasticsearchContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ namespace Testcontainers.Elasticsearch;

/// <inheritdoc cref="DockerContainer" />
[PublicAPI]
public sealed class ElasticsearchContainer : DockerContainer
public sealed class ElasticsearchContainer : DockerContainer, IDatabaseContainer
{
private readonly ElasticsearchConfiguration _configuration;

Expand Down
2 changes: 1 addition & 1 deletion src/Testcontainers.EventStoreDb/EventStoreDbContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ namespace Testcontainers.EventStoreDb;

/// <inheritdoc cref="DockerContainer" />
[PublicAPI]
public sealed class EventStoreDbContainer : DockerContainer
public sealed class EventStoreDbContainer : DockerContainer, IDatabaseContainer
{
/// <summary>
/// Initializes a new instance of the <see cref="EventStoreDbContainer" /> class.
Expand Down
2 changes: 1 addition & 1 deletion src/Testcontainers.LocalStack/LocalStackContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ namespace Testcontainers.LocalStack;

/// <inheritdoc cref="DockerContainer" />
[PublicAPI]
public sealed class LocalStackContainer : DockerContainer
public sealed class LocalStackContainer : DockerContainer, IDatabaseContainer
{
/// <summary>
/// Initializes a new instance of the <see cref="LocalStackContainer" /> class.
Expand Down
2 changes: 1 addition & 1 deletion src/Testcontainers.MariaDb/MariaDbContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ namespace Testcontainers.MariaDb;

/// <inheritdoc cref="DockerContainer" />
[PublicAPI]
public sealed class MariaDbContainer : DockerContainer
public sealed class MariaDbContainer : DockerContainer, IDatabaseContainer
{
private readonly MariaDbConfiguration _configuration;

Expand Down
2 changes: 1 addition & 1 deletion src/Testcontainers.Minio/MinioContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ namespace Testcontainers.Minio;

/// <inheritdoc cref="DockerContainer" />
[PublicAPI]
public sealed class MinioContainer : DockerContainer
public sealed class MinioContainer : DockerContainer, IDatabaseContainer
{
private readonly MinioConfiguration _configuration;

Expand Down
2 changes: 1 addition & 1 deletion src/Testcontainers.MongoDb/MongoDbContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ namespace Testcontainers.MongoDb;

/// <inheritdoc cref="DockerContainer" />
[PublicAPI]
public sealed class MongoDbContainer : DockerContainer
public sealed class MongoDbContainer : DockerContainer, IDatabaseContainer
{
private readonly MongoDbConfiguration _configuration;

Expand Down
2 changes: 1 addition & 1 deletion src/Testcontainers.MsSql/MsSqlContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ namespace Testcontainers.MsSql;

/// <inheritdoc cref="DockerContainer" />
[PublicAPI]
public sealed class MsSqlContainer : DockerContainer
public sealed class MsSqlContainer : DockerContainer, IDatabaseContainer
{
private readonly MsSqlConfiguration _configuration;

Expand Down
2 changes: 1 addition & 1 deletion src/Testcontainers.MySql/MySqlContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ namespace Testcontainers.MySql;

/// <inheritdoc cref="DockerContainer" />
[PublicAPI]
public sealed class MySqlContainer : DockerContainer
public sealed class MySqlContainer : DockerContainer, IDatabaseContainer
{
private readonly MySqlConfiguration _configuration;

Expand Down
2 changes: 1 addition & 1 deletion src/Testcontainers.Neo4j/Neo4jContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ namespace Testcontainers.Neo4j;

/// <inheritdoc cref="DockerContainer" />
[PublicAPI]
public sealed class Neo4jContainer : DockerContainer
public sealed class Neo4jContainer : DockerContainer, IDatabaseContainer
{
/// <summary>
/// Initializes a new instance of the <see cref="Neo4jContainer" /> class.
Expand Down
2 changes: 1 addition & 1 deletion src/Testcontainers.Oracle/OracleContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ namespace Testcontainers.Oracle;

/// <inheritdoc cref="DockerContainer" />
[PublicAPI]
public sealed class OracleContainer : DockerContainer
public sealed class OracleContainer : DockerContainer, IDatabaseContainer
{
private readonly OracleConfiguration _configuration;

Expand Down
2 changes: 1 addition & 1 deletion src/Testcontainers.PostgreSql/PostgreSqlContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ namespace Testcontainers.PostgreSql;

/// <inheritdoc cref="DockerContainer" />
[PublicAPI]
public sealed class PostgreSqlContainer : DockerContainer
public sealed class PostgreSqlContainer : DockerContainer, IDatabaseContainer
{
private readonly PostgreSqlConfiguration _configuration;

Expand Down
2 changes: 1 addition & 1 deletion src/Testcontainers.RabbitMq/RabbitMqContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ namespace Testcontainers.RabbitMq;

/// <inheritdoc cref="DockerContainer" />
[PublicAPI]
public sealed class RabbitMqContainer : DockerContainer
public sealed class RabbitMqContainer : DockerContainer, IDatabaseContainer
{
private readonly RabbitMqConfiguration _configuration;

Expand Down
2 changes: 1 addition & 1 deletion src/Testcontainers.RavenDb/RavenDbContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ namespace Testcontainers.RavenDb;

/// <inheritdoc cref="DockerContainer" />
[PublicAPI]
public sealed class RavenDbContainer : DockerContainer
public sealed class RavenDbContainer : DockerContainer, IDatabaseContainer
{
/// <summary>
/// Initializes a new instance of the <see cref="RavenDbContainer" /> class.
Expand Down
2 changes: 1 addition & 1 deletion src/Testcontainers.Redis/RedisContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ namespace Testcontainers.Redis;

/// <inheritdoc cref="DockerContainer" />
[PublicAPI]
public sealed class RedisContainer : DockerContainer
public sealed class RedisContainer : DockerContainer, IDatabaseContainer
{
/// <summary>
/// Initializes a new instance of the <see cref="RedisContainer" /> class.
Expand Down
2 changes: 1 addition & 1 deletion src/Testcontainers.SqlEdge/SqlEdgeContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ namespace Testcontainers.SqlEdge;

/// <inheritdoc cref="DockerContainer" />
[PublicAPI]
public sealed class SqlEdgeContainer : DockerContainer
public sealed class SqlEdgeContainer : DockerContainer, IDatabaseContainer
{
private readonly SqlEdgeConfiguration _configuration;

Expand Down
2 changes: 1 addition & 1 deletion src/Testcontainers.WebDriver/WebDriverContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ namespace Testcontainers.WebDriver;

/// <inheritdoc cref="DockerContainer" />
[PublicAPI]
public sealed class WebDriverContainer : DockerContainer
public sealed class WebDriverContainer : DockerContainer, IDatabaseContainer
{
private readonly WebDriverConfiguration _configuration;

Expand Down
17 changes: 17 additions & 0 deletions src/Testcontainers/Containers/IDatabaseContainer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace DotNet.Testcontainers.Containers
{
using JetBrains.Annotations;

/// <summary>
/// A database container instance.
/// </summary>
[PublicAPI]
public interface IDatabaseContainer
{
/// <summary>
/// Gets the connection string for connecting to the database.
/// </summary>
/// <returns>The connection string for connecting to the database.</returns>
string GetConnectionString();
}
}
1 change: 1 addition & 0 deletions tests/Testcontainers.Databases.Tests/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
root = true
25 changes: 25 additions & 0 deletions tests/Testcontainers.Databases.Tests/DatabasesContainerTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
namespace Testcontainers.Databases;

public sealed class DatabaseContainersTest
{
[Theory]
[MemberData(nameof(DatabaseContainersTheoryData))]
public void ImplementsIDatabaseContainerInterface(Type type)
{
Assert.True(type.IsAssignableTo(typeof(IDatabaseContainer)));
}

public static IEnumerable<object[]> DatabaseContainersTheoryData
{
get
{
static bool HasGetConnectionStringMethod(Type type) => type.IsAssignableTo(typeof(IContainer)) && type.GetMethod("GetConnectionString") != null;
var assembly = typeof(DatabaseContainersTest).Assembly;
var dependencyContext = DependencyContext.Load(assembly) ?? throw new InvalidOperationException($"DependencyContext.Load({assembly}) returned null");
return dependencyContext.RuntimeLibraries
.Where(library => library.Name.StartsWith("Testcontainers."))
.SelectMany(library => Assembly.Load(library.Name).GetExportedTypes().Where(HasGetConnectionStringMethod))
.Select(type => new[] { type });
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net6.0</TargetFrameworks>
<IsPackable>false</IsPackable>
<IsPublishable>false</IsPublishable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="7.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
<PackageReference Include="coverlet.collector" Version="3.2.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5" />
<PackageReference Include="xunit" Version="2.4.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(SolutionDir)src/Testcontainers.*/Testcontainers.*.csproj" />
</ItemGroup>
</Project>
7 changes: 7 additions & 0 deletions tests/Testcontainers.Databases.Tests/Usings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
global using System;
global using System.Collections.Generic;
global using System.Linq;
global using System.Reflection;
global using DotNet.Testcontainers.Containers;
global using Microsoft.Extensions.DependencyModel;
global using Xunit;

0 comments on commit ebc03d7

Please sign in to comment.