是否可以在不指定数据库的情况下连接到SQL Server?

时间:2014-07-06 20:33:59

标签: c# sql sql-server unit-testing

我正在尝试为连接到SQL Server以实现持久性的代码编写一些单元测试,但我希望能够通过将其指向SQL Server实例来运行我的单元测试,并让测试创建他们自己的数据库运行测试,因此在每次测试之后它可以在下一次测试重新创建它之前删除数据库然后进行设置,因此我知道没有遗留数据或结构遗留在上一次测试中影响下一次测试。

5 个答案:

答案 0 :(得分:5)

简而言之:不,你做不到。您可以从连接字符串中省略数据库,但在这种情况下,将连接到连接到SQL Server的登录的已配置默认数据库(并且该默认数据库必须在建立连接时存在)

如果您想拥有此方案,则需要

  1. 首先连接到您的实例和数据库master并创建新的testdb(或其他任何名称)

  2. 断开

  3. 在测试中,连接到实例和testdb数据库

  4. 更好的是:使用某种类型的模拟框架,以便在测试场景中甚至不需要实际的数据库!

答案 1 :(得分:1)

使用模拟框架可以实现的目标,在这种情况下,您甚至不必“连接到数据库”,只需模拟数据库应返回的返回值,以便您测试“db handler”实现。

在C#中有几种可供选择,我可以推荐Rhino Mocks和Moq来命名两种。这是一个详细说明问题的问题; https://stackoverflow.com/questions/37359/what-c-sharp-mocking-framework-to-use

答案 2 :(得分:1)

我使用以下类来促进OP的场景:

public class MsSqlDatabaseCreator
{
    public void Create(string connectionstring)
    {
        if (DatabaseExists(connectionstring))
        {
            DropDatabase(connectionstring);
        }
        CreateDatabase(connectionstring);
    }

    private static void CreateDatabase(string connectionString)
    {
        var sqlConnectionStringBuilder = new SqlConnectionStringBuilder(connectionString);

        var databaseName = sqlConnectionStringBuilder.InitialCatalog;

        sqlConnectionStringBuilder.InitialCatalog = "master";

        using (var sqlConnection = new SqlConnection(sqlConnectionStringBuilder.ConnectionString))
        {
            sqlConnection.Open();

            using (var sqlCommand = sqlConnection.CreateCommand())
            {
                sqlCommand.CommandText = $"CREATE DATABASE {databaseName}";
                sqlCommand.ExecuteNonQuery();
            }
        }
    }

    private static bool DatabaseExists(string connectionString)
    {
        var sqlConnectionStringBuilder = new SqlConnectionStringBuilder(connectionString);

        var databaseName = sqlConnectionStringBuilder.InitialCatalog;

        sqlConnectionStringBuilder.InitialCatalog = "master";

        using (var sqlConnection = new SqlConnection(sqlConnectionStringBuilder.ConnectionString))
        {
            sqlConnection.Open();

            using (var command = sqlConnection.CreateCommand())
            {
                command.CommandText = $"SELECT db_id('{databaseName}')";

                return command.ExecuteScalar() != DBNull.Value;
            }
        }
    }

    private static void DropDatabase(string connectionString)
    {
        var sqlConnectionStringBuilder = new SqlConnectionStringBuilder(connectionString);

        var databaseName = sqlConnectionStringBuilder.InitialCatalog;

        sqlConnectionStringBuilder.InitialCatalog = "master";

        using (var sqlConnection = new SqlConnection(sqlConnectionStringBuilder.ConnectionString))
        {
            sqlConnection.Open();

            using (var sqlCommand = sqlConnection.CreateCommand())
            {
                sqlCommand.CommandText = $@"
                    ALTER DATABASE {databaseName} SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
                    DROP DATABASE [{databaseName}]
                ";
                sqlCommand.ExecuteNonQuery();
            }
        }
    }
}

重要的部分是将数据库名称(初始目录)切换为master。这样你就可以只有一个连接字符串。

答案 3 :(得分:0)

为什么没有专门用于测试的命名数据库?并且每次都创建它。通过这种方式,您不必使用连接字符串 - 它始终是相同的。

然而,有一个更好的解决方案:在所有测试中,启动事务,进行测试,数据搞砸了。验证(或失败)测试后,请展开交易。这样您就不需要为每次测试都删除创建测试,因为数据永远不会更改。

但是您需要确保测试数据库中的模式始终是最新的。因此,每当架构发生变化时,您都需要删除创建测试数据库。

blogged about database tests以及我们如何处理实体框架迁移。这可能不完全适用于您的情况,但可能有助于提出想法。

关于在测试中使用模拟 - 是的,这是绝对有效的建议,应该在大多数时间都遵循。除非您尝试测试数据库层。在这种情况下,没有模拟会拯救你,你只需要去DB。很多次我尝试在EF中模拟DbContext,但从未设法模拟真实的DB行为。所以去DB更容易,而不是模拟DB-mock。

答案 4 :(得分:0)

我使用SQL Server Management Objects完成任务。它的Server and Database APIs并不一定需要连接字符串,但我认为您可能仍需要指定数据库。您可以使用master。 (同时检查jeroenh's answer about creating object using SMO API

顺便说一句,如果您使用.Net 4.0.2及更高版本,您也可以使用LocalDB,这样会更好。

编辑:请注意,实际上LocalDB是SQL Server 2012功能you still need .Net Framework > 4.0.2 to be able to use it