首页 > 解决方案 > C# 中的单元测试数据库命令

问题描述

我目前正在尝试为将数据插入数据库的方法创建单元测试。我知道我需要模拟或创建一个假类,以便我的测试数据不会添加到我的数据库中,但我不知道如何去做。有人有想法吗?

这是我的控制台“用户界面”触及的代码。这是在我的“商业”C# 项目中:

        /// <summary>
        /// Gets the connection string.
        /// </summary>
        private static readonly string _connectionString = ConfigurationManager.ConnectionStrings["Database"].ConnectionString;

        /// <summary>
        /// Inserts a new admin into the Admins database table.
        /// </summary>
        /// <param name="admin">Admin record.</param>
        public static void InsertNewAdmin(Admin admin)
        {
            var adminDatabaseWriter = new AdminDatabaseWriter(_connectionString);
            adminDatabaseWriter.Insert(admin);
        }

从那里它进入我的 AdminDatabaseWriter 类并在方法“Insert”中执行以下操作:

        /// <summary>
        /// Inserts a new admin into the Admins database table.
        /// </summary>
        /// <param name="admin">Admin record.</param>
        public void Insert(Admin admin)
        {
            using SqlConnection sqlConnection = new(_connectionString);
            sqlConnection.Open();

            using SqlCommand sqlCommand = DatabaseHelper.CreateNewSqlCommandWithStoredProcedure("InsertNewAdmin", sqlConnection);
            sqlCommand.Parameters.AddWithValue("@PIN", admin.PIN);
            sqlCommand.Parameters.AddWithValue("@AdminType", admin.AdminTypeCode);
            sqlCommand.Parameters.AddWithValue("@FirstName", admin.FirstName);
            sqlCommand.Parameters.AddWithValue("@LastName", admin.LastName);
            sqlCommand.Parameters.AddWithValue("@EmailAddress", admin.EmailAddress);
            sqlCommand.Parameters.AddWithValue("@Password", admin.Password);
            sqlCommand.Parameters.AddWithValue("@AssessmentScore", admin.AssessmentScore);

            var userAddedSuccessfully = sqlCommand.ExecuteNonQuery();
            sqlConnection.Close();

            if (userAddedSuccessfully < 0)
            {
                throw new AdminNotAddedToDatabaseException("User was unsuccessful at being uploaded to the database for an unknown reason.");
            }
        }

同样,我正在尝试对我附加的最后一段代码进行单元测试。测试我的代码是否将对象实际添加到数据库中而不实际将其添加到数据库中的最佳方法是什么。

标签: c#sql.netunit-testingmocking

解决方案


在为方法编写单元测试之前Insert,你应该问问自己你到底想要“单元测试”什么。Insert什么都不返回(void),所以没有返回值来测试。但是,它在满足某些条件时会抛出异常,并且它还有一个副作用(调用ExecuteNonQuery将某些数据放入数据库)。

无论您决定测试什么场景(抛出异常或发生副作用),您都应该告诉您的测试框架,当它到达发生副作用的行时,它应该用另一个实际上什么都不做的操作替换它(不仅这样实际的数据库不会变得“脏”,而且还因为外部依赖项可能会消耗时间,甚至更糟,使您的测试失败,因为它们自己失败了,基本上我们不关心在上下文中的这种依赖项我们测试的对象)。

所有标准测试框架/库都提供了用于对异常和副作用进行断言的工具,但请注意,模拟副作用(通常是模拟依赖项)不仅在针对返回值或异常进行测试时必不可少,而且当您希望验证副作用本身发生。因为无论哪种方式,我们都希望防止外部依赖项干扰 out 测试。

无论您使用什么测试框架,通常首选在产品代码中使用接口,以促进应用程序的灵活性和可测试性,并且它还可以让您模拟“副作用”方法,例如 ExecuteNonQuery(that isISqlCommand而不是SqlCommand)。这是因为当您指示模拟接口方法时,框架会创建一个新的虚拟类来实现相同的接口。

总而言之,可以通过以下方式测试该对象是否“添加”到数据库中:

  1. 更改代码以使用接口(ISqlConnection、ISqlCommand)。
  2. 模拟所需接口的方法 ( Open, ExecuteNonQuery, Close)。
  3. 执行Insert
  4. 验证ExecuteNonQuery已被调用。

(对象是否被正确添加是另一回事,这可能需要集成测试。)


推荐阅读