Skip to content

Instantly share code, notes, and snippets.

@BrianVallelunga
Created October 4, 2017 17:02
Show Gist options
  • Select an option

  • Save BrianVallelunga/83b13a9636e96c5ebff477fb5444482c to your computer and use it in GitHub Desktop.

Select an option

Save BrianVallelunga/83b13a9636e96c5ebff477fb5444482c to your computer and use it in GitHub Desktop.
Snapshot support for integrated testing resets
namespace IntegrationTests
{
using System;
using System.Data.SqlClient;
using System.IO;
using Xunit;
public class DatabaseFixture : IDisposable
{
private const string databaseName = "mydb-test";
private readonly string masterConnectionString;
private readonly string testConnectionString;
public DatabaseFixture()
{
this.testConnectionString = "connection string to your test db on the server";
this.masterConnectionString = "connection string to your master db on the server";
this.DropTestDatabase();
this.CreateTestDatabase();
this.CreateSnapshotDatabase();
}
public void RestoreTestDatabaseFromSnapshot()
{
Console.WriteLine($"Restoring test database from snapshot");
string sql = $@"
ALTER DATABASE [{databaseName}] SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
RESTORE DATABASE [{databaseName}] FROM DATABASE_SNAPSHOT = '{databaseName}-snapshot';
ALTER DATABASE [{databaseName}] SET MULTI_USER;";
using (var sqlDbConnection = new SqlConnection(this.masterConnectionString))
{
sqlDbConnection.Open();
var sqlCommand = new SqlCommand(sql, sqlDbConnection);
sqlCommand.ExecuteNonQuery();
}
}
private void CreateTestDatabase()
{
Console.WriteLine($"Creating test database");
// Do any initial DB schema/test data loading here
}
private void DropSnapshotDatabase()
{
Console.WriteLine($"Dropping snapshot database if it exists");
using (var sqlDbConnection = new SqlConnection(this.masterConnectionString))
{
sqlDbConnection.Open();
var sqlCommand = new SqlCommand($"DROP DATABASE IF EXISTS [{databaseName}-snapshot]", sqlDbConnection);
sqlCommand.ExecuteNonQuery();
}
}
private void DropTestDatabase()
{
Console.WriteLine($"Dropping test database if it exists");
// Must drop the snapshot if it exists first
this.DropSnapshotDatabase();
using (var sqlDbConnection = new SqlConnection(this.masterConnectionString))
{
sqlDbConnection.Open();
var sqlCommand = new SqlCommand($"DROP DATABASE IF EXISTS [{databaseName}]", sqlDbConnection);
sqlCommand.ExecuteNonQuery();
}
}
private void CreateSnapshotDatabase()
{
using (var sqlDbConnection = new SqlConnection(this.masterConnectionString))
{
sqlDbConnection.Open();
// Get the file name of the database
var dbFilePathCommand = new SqlCommand($"SELECT physical_name FROM sys.master_files where [name] = '${databaseName}'");
string databaseFilePath = sqlCommand.ExecuteScalar().ToString();
// Create a snapshot with a different file name
string snapshotFilePath = databaseFilePath.Replace(".mdf", "-snapshot.ss");
Console.WriteLine($"Taking snapshot of test database");
string sql = $@"
CREATE DATABASE [{databaseName}-snapshot] ON
(NAME = [{databaseName}], FILENAME = '{snapshotFilePath}')
AS SNAPSHOT of [{databaseName}];";
var sqlCommand = new SqlCommand(sql, sqlDbConnection);
sqlCommand.ExecuteNonQuery();
}
}
public void Dispose()
{
// Delete a snapshot if it exists
this.DropSnapshotDatabase();
}
}
/// <summary>
/// This allows the <see cref="DatabaseFixture"/> to last the lifetime of all tests that have this attribute.
/// </summary>
[CollectionDefinition(nameof(DatabaseFixtureCollection))]
public class DatabaseFixtureCollection : ICollectionFixture<DatabaseFixture>
{
// This class has no code, and is never created. Its purpose is simply
// to be the place to apply [CollectionDefinition] and all the
// ICollectionFixture<> interfaces.
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment