예제 #1
0
        // ReSharper disable once UnusedMember.Global
        public DbTestFixture()
        {
            this.dbTestSetup = DbTestSetupFinder.DbTestSetup;
            if (dbTestSetup == null)
            {
                throw new Exception("Failed to create test fixture. Unable to determine DbTest setup class.");
            }
            this.ServiceProvider = dbTestSetup.ProduceServiceProvider();

            this.config     = ServiceProvider.GetService <IDbTestRunnerConfiguration>();
            this.scaffolder = ServiceProvider.GetService <IDbScaffolder>();
            this.context    = ServiceProvider.GetService <IDbTestRunnerContext>();
            this.context.ProduceSqlParametersForCustomTypes = dbTestSetup.ProduceSqlParametersForCustomTypes;

            // if we're not supposed to run DbTests, then don't bother setting up the runner.
            if (!this.config.Enabled())
            {
                return;
            }


            // ensure the db exists, has latest migrations, etc.
            var initializationResult = InitializeDatabase();

            if (!initializationResult.IsSuccessful)
            {
                throw new DbTestException("Failed to intialize database for tests.");
            }
        }
예제 #2
0
        private DbTestSqlResult ExecuteAct(IDbTestRunnerContext context, DbTestRunnerResult result, IDisposable dbHandle)
        {
            var request = OnAct(context, dbHandle, this.TestOutputHelper);

            result.TestPhase = "ACT";
            return(ExecuteTestPhase(result, "ACT", request));
        }
예제 #3
0
        protected override void RegisterServices(IServiceCollection services, IDbTestRunnerContext context, IDbTestRunnerConfiguration config, IDbScaffolder scaffolder)
        {
            var efScaffolder = scaffolder as EfDbScaffolder;
            var efConfig     = config as DbTestRunnerConfiguration;

            efScaffolder?.RegisterServices <TDataContext>(services, context, efConfig);
        }
예제 #4
0
        protected SqlParameter[] GetParametersForDbObject(string objectName, IDbTestRunnerContext runnerContext)
        {
            var parameters = new List <SqlParameter>();
            var sql        = @" Select p.name, t.name 
                               From sys.parameters p
                               inner join sys.types t on p.system_type_id = t.system_type_id 
                               where Object_Id = OBJECT_ID(@objname)
                               order by p.parameter_id";
            var pObjName   = new SqlParameter("@objname", objectName);

            using (var cmd = GetDbConnection().CreateCommand())
            {
                if (cmd.Connection.State != ConnectionState.Open)
                {
                    cmd.Connection.Open();
                }

                cmd.CommandType = CommandType.Text;
                cmd.CommandText = sql;
                cmd.Parameters.Add(pObjName);
                using (var rdr = cmd.ExecuteReader())
                {
                    while (rdr.Read())
                    {
                        var parameterName = rdr[0].ToString();
                        var parameterType = rdr[1].ToString();
                        parameters.Add(this.GetSqlParameterWithDefaultValue(parameterName, parameterType, runnerContext));
                    }
                }
            }

            return(parameters.ToArray());
        }
예제 #5
0
        /// <summary>
        /// Drops the database specified in the context's DBName property
        /// </summary>
        public void DropDatabase(IDbTestRunnerContext context, IDbTestRunnerConfiguration config)
        {
            if (context == null)
            {
                return;
            }

            try
            {
                var connString = ProduceConnectionString(config, "master");
                using (var conn = new SqlConnection(connString))
                {
                    conn.Open();
                    using (var cmd = conn.CreateCommand())
                    {
                        cmd.CommandType = CommandType.Text;

                        cmd.CommandText = $"Drop Database {context.DbName}";
                        cmd.ExecuteNonQuery();
                    }
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                throw;
            }
        }
예제 #6
0
        public override void TearDownDatabase(IDbTestRunnerContext context, IDbTestRunnerConfiguration config, bool allTestsWereSuccessful)
        {
            var sqlConnection = new SqlConnection(context.ConnectionString);

            SqlConnection.ClearPool(sqlConnection);

            if ((allTestsWereSuccessful && config.DropDatabaseOnSuccess) ||
                (!allTestsWereSuccessful && config.DropDatabaseOnFailure))
            {
                context.DbScaffolder.DropDatabase(context, config);
            }
        }
예제 #7
0
 /// <summary>
 /// Executes all the EF data context migrations against the configured database
 /// </summary>
 private void EnsureMigrations(DbTestRunnerConfiguration config, IDbTestRunnerContext context, DbInitializationResult result)
 {
     try
     {
         using (var scope = context.ServiceProvider.CreateScope())
         {
             var migrationRunner = scope.ServiceProvider.GetService <DataMigrationRunner>();
             migrationRunner.ApplyMigrations(config.DataContextType);
             result.Logs.Add("DB Migrations completed.");
             result.IsSuccessful = true;
         }
     }
     catch (Exception e)
     {
         result.Logs.Add($"Encountered exception running migrations. Exception: {e.Message} Details: {e}");
         throw;
     }
 }
예제 #8
0
        /// <summary>
        /// Creates the database specified in Context.DbName
        /// </summary>
        protected void CreateDatabase(IDbTestRunnerConfiguration config, IDbTestRunnerContext context)
        {
            var    connStr = this.ProduceConnectionString(config, "master");
            string sql     = $"CREATE DATABASE {context.DbName}";

            using (var conn = new SqlConnection(connStr))
            {
                conn.Open();
                using (var cmd = conn.CreateCommand())
                {
                    cmd.CommandText = sql;
                    cmd.CommandType = CommandType.Text;
                    cmd.ExecuteNonQuery();
                }

                conn.Close();
            }
        }
예제 #9
0
        public override void TearDownDatabase(IDbTestRunnerContext context, IDbTestRunnerConfiguration config, bool allTestsWereSuccessful)
        {
            if (!(config is DbTestRunnerConfiguration efConfig))
            {
                return;
            }

            // clears the connection pool for the specified connection
            // this lets us drop the database without having hanging active connections
            using (var dataContext = context.ProduceDbContext(efConfig.DataContextType))
            {
                if (dataContext.Database.GetDbConnection() is SqlConnection sqlConnection)
                {
                    SqlConnection.ClearPool(sqlConnection);
                }
            }

            if ((allTestsWereSuccessful && config.DropDatabaseOnSuccess) ||
                (!allTestsWereSuccessful && config.DropDatabaseOnFailure))
            {
                context.DbScaffolder.DropDatabase(context, config);
            }
        }
예제 #10
0
 /// <summary>
 /// Invokes the ASSERT hook method
 /// </summary>
 // ReSharper disable once UnusedParameter.Local
 private void ExecuteAssert(IDbTestRunnerContext context, DbTestRunnerResult result, List <DataTable> data, IDisposable dbHandle)
 {
     try
     {
         result.TestPhase    = "ASSERT";
         result.IsSuccessful = true;
         result.Logs.Add("ASSERT Phase: Running test Assertions.");
         this.OnAssert(data == null || !data.Any() ? null : data[0], dbHandle, data?.ToArray(), this.TestOutputHelper);
         result.Logs.Add("ASSERT Phase: All assertions passed.");
     }
     catch (XunitException)
     {
         // we actually just want to re-throw the current exception to maintain the stack trace
         throw;
     }
     catch (Exception e)
     {
         // non-assertion exception occurred.
         result.Logs.Add($"Error asserting state of data. {e}");
         result.Exception    = e;
         result.IsSuccessful = false;
     }
 }
예제 #11
0
        /// <summary>
        /// Configures all the services necessary to produce the data context from the IoC container.
        /// </summary>
        private void ConfigureDataContext <TDataContext>(IServiceCollection services, IDbTestRunnerContext context, DbTestRunnerConfiguration config) where TDataContext : DbContext
        {
            // Our data contexts require loggers, so add the loggers to the services collection
            services.AddLogging();

            // if a db name is specified, use that, otherwise generate a unique database name
            context.DbName = context.DbName ?? config.DbName ?? ProduceUniqueDatabaseName <TDataContext>();

            // build an ado.net connection string for the server and database configured.
            // turn off connection pooling to prevent it from holding on to the connection after we're done using it.
            context.ConnectionString = ProduceConnectionString(config, context.DbName, useConnectionPooling: false);

            var connectionString = context.ConnectionString;

            services.AddDbContext <TDataContext>(options => { options.UseSqlServer(connectionString); });


            // register the data migration runner.
            services.AddDataMigrationRunner();
        }
예제 #12
0
 protected virtual SqlParameter ProduceSqlParametersForCustomTypes(string name, string type, IDbTestRunnerContext runnerContext)
 {
     return(runnerContext.ProduceSqlParametersForCustomTypes?.Invoke(name, type));
 }
예제 #13
0
        private SqlParameter GetSqlParameterWithDefaultValue(string name, string type, IDbTestRunnerContext runnerContext)
        {
            switch (type)
            {
            case "bit":
                return(new SqlParameter(name, false));

            case "int":
            case "byte":
            case "tinyint":
            case "smallint":
            case "bigint":
            case "numeric":
                return(new SqlParameter(name, -1));

            case "decimal":
            case "float":
            case "smallmoney":
                return(new SqlParameter(name, -1m));

            case "date":
            case "datetime":
            case "datetime2":
            case "smalldatetime":
            case "timestamp":

                return(new SqlParameter(name, System.Data.SqlTypes.SqlDateTime.MinValue.Value.AddSeconds(1)));

            case "text":
            case "ntext":
            case "varchar":
            case "nvarchar":
            case "char":
            case "nchar":
            case "xml":
                return(new SqlParameter(name, ""));

            default:
            {
                var customTypeParameter = ProduceSqlParametersForCustomTypes(name, type, runnerContext);
                return(customTypeParameter ?? new SqlParameter(name, null));
            }
            }
        }
예제 #14
0
        protected override void InitializeSchemaAndData(IDbTestRunnerConfiguration config, IDbTestRunnerContext context, DbInitializationResult result)
        {
            if (!(config is DbTestRunnerConfiguration efConfig))
            {
                result.IsSuccessful = false;
                result.Logs.Add($"Failed to initialize schema and database.  Configuration is not of the expected type. Expected: {typeof(DbTestRunnerConfiguration).FullName} actual: {config.GetType().FullName}");
                return;
            }

            result.Logs.Add($"Preparing Database with {efConfig.DataContextType} migrations.");
            EnsureMigrations(efConfig, context, result);
        }
예제 #15
0
 /// <summary>
 /// Return the sql text to execute as part of the ACT phase of the test.
 /// Or you may modify the data using the provided connection
 /// </summary>
 /// <returns>the sql text to execute</returns>
 protected abstract SqlRequest Act(IDbTestRunnerContext context, SqlConnection connection, ITestOutputHelper testOutputHelper);
예제 #16
0
            protected override SqlRequest Act(IDbTestRunnerContext context, TDataContext dataContext, ITestOutputHelper testOutputHelper)
            {
                var functionParameters = GetParametersForDbObject(this.FunctionName, context);

                return(this.InvokeTableFunction(this.FunctionName, this.SchemaName, functionParameters));
            }
예제 #17
0
 /// <summary>
 /// ACT on the state of the data.
 /// This is where you should exercise the db object under test.
 /// The data returned from this code will be presented to the Assert method.
 /// </summary>
 protected abstract SqlRequest OnAct(IDbTestRunnerContext context, IDisposable dbHandle, ITestOutputHelper testOutputHelper);
예제 #18
0
 protected override SqlRequest OnAct(IDbTestRunnerContext context, IDisposable dbHandle, ITestOutputHelper testOutputHelper)
 {
     return(Act(context, ToDataContext(dbHandle), testOutputHelper));
 }
예제 #19
0
 protected override SqlRequest OnArrange(IDbTestRunnerContext context, IDisposable dbHandle, ITestOutputHelper testOutputHelper)
 {
     return(Arrange(context, ToSqlConnection(dbHandle), testOutputHelper));
 }
예제 #20
0
 public void RegisterDataContext(IServiceCollection services, IDbTestRunnerContext context, DbTestRunnerConfiguration config, IEfDbScaffolder scaffolder)
 {
     scaffolder.RegisterServices <TDataContext>(services, context, config);
 }
예제 #21
0
 public static DbContext ProduceDbContext(this IDbTestRunnerContext context, Type dataContextType)
 {
     return(context.ServiceProvider.CreateScope().ServiceProvider.GetService(dataContextType) as DbContext);
 }
예제 #22
0
 protected virtual void InitializeSchemaAndData(IDbTestRunnerConfiguration config, IDbTestRunnerContext context, DbInitializationResult result)
 {
     result.IsSuccessful = true;
 }
예제 #23
0
        protected override void InitializeSchemaAndData(IDbTestRunnerConfiguration config, IDbTestRunnerContext context, DbInitializationResult result)
        {
            if (!(config is DbTestRunnerConfiguration adoConfig))
            {
                result.IsSuccessful = false;
                result.Logs.Add($"Failed to initialize schema and database.  Configuration is not of the expected type. Expected: {typeof(DbTestRunnerConfiguration).FullName} actual: {config.GetType().FullName}");
                return;
            }

            var connStr = this.ProduceConnectionString(config, context.DbName);

            if (string.IsNullOrEmpty(connStr))
            {
                result.IsSuccessful = false;
                result.Logs.Add("Failed to initialize schema and database.  Configuration string is empty or null.");
                return;
            }

            result.Logs.Add($"Attempting to initialize DB Schema and Data using connection string: {connStr}");
            result.IsSuccessful = adoConfig.InitializeDbSchemaAndData(connStr, result.WasDatabaseCreated);
        }
예제 #24
0
 protected override SqlRequest Act(IDbTestRunnerContext context, SqlConnection connection, ITestOutputHelper testOutputHelper)
 {
     // return just an empty data set so we can examine the shape of the return object.
     return(SqlRequest.ReturnNoRows(this.GetQualifiedTableOrViewName <TEntity>(this.TableOrViewName, this.Schema)));
 }
예제 #25
0
 public static TDataContext ProduceDataContext <TDataContext>(this IDbTestRunnerContext context) where TDataContext : DbContext
 {
     return((TDataContext)ProduceDbContext(context, typeof(TDataContext)));
 }
예제 #26
0
        /// <summary>
        /// Provisions the database specified in Context.DbName
        /// Creates the DB if it does not exist and runs all
        /// data context migrations against the database.
        /// </summary>
        public DbInitializationResult InitializeDb(IDbTestRunnerConfiguration config, IDbTestRunnerContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            if (config == null)
            {
                throw new ArgumentNullException(nameof(config));
            }

            var result = new DbInitializationResult();

            try
            {
                if (string.IsNullOrEmpty(context.DbName) || !DoesDatabaseExist(config, context.DbName))
                {
                    result.Logs.Add($"Creating new Test database {context.DbName}.");
                    CreateDatabase(config, context);
                    result.WasDatabaseCreated = true;
                }
                else
                {
                    result.Logs.Add($"Using existing database {context.DbName}.");
                }



                try
                {
                    context.ConnectionString = ProduceConnectionString(config, context.DbName);
                    InitializeSchemaAndData(config, context, result);
                    if (!result.IsSuccessful)
                    {
                        result.Logs.Add("Failed to initialize db schema and data.");
                        result.IsSuccessful = false;
                    }
                }
                catch (Exception e)
                {
                    result.Exception = e;
                    result.Logs.Add("Failed to initialize db schema and data.");
                    result.IsSuccessful = false;
                }

                if (result.IsSuccessful)
                {
                    result.Logs.Add($"Database {context.DbName} has been prepared for tests.");
                    result.IsSuccessful = true;
                }
            }
            catch (Exception e)
            {
                result.Exception = e;
                result.Logs.Add($"Error attempting to Initialize DB. {e}");
                result.IsSuccessful = false;
            }

            return(result);
        }
예제 #27
0
 /// <summary>
 /// (hook)
 /// Return the sql text to execute as part of the ACT phase of the test.
 /// Or you may modify the data context directly.
 /// </summary>
 /// <returns>the sql text to execute</returns>
 protected abstract SqlRequest Act(IDbTestRunnerContext context, TDataContext dataContext, ITestOutputHelper testOutputHelper);
예제 #28
0
 protected override SqlRequest Arrange(IDbTestRunnerContext context, SqlConnection connection, ITestOutputHelper testOutputHelper)
 {
     return(this.DoNothing());
 }
예제 #29
0
            protected override SqlRequest Act(IDbTestRunnerContext context, SqlConnection connection, ITestOutputHelper testOutputHelper)
            {
                var parameters = this.GetParametersForDbObject(this.StoredProcedureName, context);

                return(this.ExecuteStoredProcedure(this.DbObjectName, this.SchemaName, parameters));
            }
예제 #30
0
 /// <summary>
 /// Participates in the registration of services
 /// during DbTestRunner startup.
 ///
 /// Provides an opportunity to inject data related
 /// services into the IoC container.
 /// </summary>
 public void RegisterServices <TDataContext>(IServiceCollection services, IDbTestRunnerContext context, DbTestRunnerConfiguration config) where TDataContext : DbContext
 {
     // produce and register the data context.
     ConfigureDataContext <TDataContext>(services, context, config);
 }