Skip to content

From .NET Tests and Apps ​

pgembed keeps its instances where MysticMind.PostgresEmbed looks for named instances. A test that asks the library for InstanceName = "app" attaches in milliseconds when pgembed start app already runs it, and starts it itself when it does not. Only the process that started a server stops it, so tests leave your instance running.

These samples are an xUnit project that CI runs against real Postgres.

An xUnit fixture ​

csharp
// The "app" instance for a test run. If `pgembed start app` already runs it,
// the tests attach in milliseconds and leave it running; otherwise they start
// it, and stop it when they finish.
public sealed class AppDatabase : IAsyncLifetime
{
    private readonly PgServer _server = new(new PgServerOptions
    {
        PgVersion = "17",
        InstanceName = "app"
    });

    public string ConnectionString => _server.ConnectionString;

    public async ValueTask InitializeAsync() => await _server.StartAsync();

    public async ValueTask DisposeAsync() => await _server.DisposeAsync();
}
snippet source | anchor
csharp
public class BookTests(AppDatabase database) : IClassFixture<AppDatabase>
{
    [Fact]
    public async Task Stores_a_book()
    {
        await using var connection = new NpgsqlConnection(database.ConnectionString);
        await connection.OpenAsync(TestContext.Current.CancellationToken);

        await using var command = new NpgsqlCommand("""
            CREATE TABLE IF NOT EXISTS books(id int PRIMARY KEY, title text);
            INSERT INTO books VALUES (1, 'Dune') ON CONFLICT DO NOTHING;
            SELECT title FROM books WHERE id = 1;
            """, connection);

        var title = await command.ExecuteScalarAsync(TestContext.Current.CancellationToken);
        Assert.Equal("Dune", title);
    }
}
snippet source | anchor

Every Postgres version ​

Start the instances once with pgembed start ci --version 16,17,18, or let each test start its own:

csharp
// `pgembed start ci --version 16,17,18` runs ci-16, ci-17 and ci-18;
// without it, each test starts its own
[Theory]
[InlineData("16")]
[InlineData("17")]
[InlineData("18")]
public async Task Runs_on_every_supported_major(string major)
{
    await using var server = new PgServer(new PgServerOptions
    {
        PgVersion = major,
        InstanceName = $"ci-{major}"
    });
    await server.StartAsync(TestContext.Current.CancellationToken);

    await using var connection = new NpgsqlConnection(server.ConnectionString);
    await connection.OpenAsync(TestContext.Current.CancellationToken);
    Assert.StartsWith($"{major}.", connection.PostgreSqlVersion.ToString());
}
snippet source | anchor

From an app ​

Pass the connection string pgembed prints, here through configuration:

csharp
// set by: export ConnectionStrings__App="$(pgembed connstr app --format npgsql)"
var connectionString = Environment.GetEnvironmentVariable("ConnectionStrings__App");

await using var dataSource = NpgsqlDataSource.Create(connectionString!);
await using var command = dataSource.CreateCommand("SELECT current_database()");
var database = (string?)await command.ExecuteScalarAsync();
snippet source | anchor

pgembed connstr app prints it in every format: Npgsql, postgresql:// URI, libpq, JDBC, and PG* environment variables.

Released under the MIT License.