Created
October 4, 2017 17:02
-
-
Save BrianVallelunga/83b13a9636e96c5ebff477fb5444482c to your computer and use it in GitHub Desktop.
Snapshot support for integrated testing resets
This file contains hidden or 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
| 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