【问题标题】:Is it possible to connect to SQL Server without specifying a database?是否可以在不指定数据库的情况下连接到 SQL Server?
【发布时间】:2016-09-15 07:02:04
【问题描述】:

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

【问题讨论】:

  • 为什么不使用模拟?
  • Mocks 固然很酷,但是用真实的数据库测试你的代码很有价值,尤其是在使用瘦 ORM 包装器时

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


【解决方案1】:

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

如果你想有这个场景,你需要

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

  2. 断开连接

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

更好的是:使用某种模拟框架,因此您甚至不需要在测试场景中使用实际数据库!

【讨论】:

  • 我们在业务逻辑单元测试中模拟精简 orm 对象,但是为了测试我们的精简 orm,最好将它们与真实数据库进行测试,并且我们也可以通过这种方式在单元测试中对它们进行性能测试,使确保我们有正确的结构和索引等
  • @0xor1 你描述的是Integration Tests 不是单元测试。单元测试应该很薄,他们应该只测试他们应该测试的层,而不是层的横截面。有些工具在单元测试方面表现出色,但在集成测试方面很糟糕,有些工具在集成测试方面表现出色,但在单元测试方面却很糟糕。
  • 随便你怎么称呼都可以,但是 orm 包装器太薄了,它们只是我们与数据库的连接,如果我们可以使用我们的签入自动测试它们,那就更好了,现在我们可以。
【解决方案2】:

我使用以下类来方便 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】:

    使用模拟框架可以实现您想要完成的任务,在这种情况下,您甚至不必“连接到数据库”,您只需模拟数据库应返回的返回值,以便您测试您的“数据库处理程序”实现。

    在 C# 方面有多种选择,我可以推荐 Rhino Mocks 和 Moq 来举两个。这是一个更详细的问题; https://stackoverflow.com/questions/37359/what-c-sharp-mocking-framework-to-use

    【讨论】:

      【解决方案4】:

      为什么不使用专用于测试的同名数据库?并且每次都创建它。这样你就不需要弄乱连接字符串了——它总是一样的。

      然而,有一个更好的解决方案:在你所有的测试中,开始事务,做你的测试,你的数据被搞砸了。验证(或失败)测试后,展开事务。这样您就不需要为每个测试创建测试,因为数据永远不会改变。

      但您需要确保 test-database 中的架构始终是最新的。因此,每当您的架构发生更改时,您都需要删除创建测试数据库。

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

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

      【讨论】:

        【解决方案5】:

        我会使用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

        【讨论】:

        • 实际上,“LocalDB”不是任何特定 .NET 框架的功能 - 它是 SQL Server 2012 和更新版本的功能
        猜你喜欢
        • 1970-01-01
        • 2015-07-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-12-27
        相关资源
        最近更新 更多