Home > front end >  Unit Test Database Commands in C#
Unit Test Database Commands in C#

Time:10-10

I am currently trying to create unit tests for my method that is inserting data into my database. I know that I need to either mock or create a fake class so that my test data does not into added to my database, but I am not sure how to go about doing this. Does anyone have an idea?

Here is my code that my console "User Interface" touches. This is in my "Business" C# project:

        /// <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);
        }

From there it goes into my AdminDatabaseWriter class and does the following in the method "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.");
            }
        }

Again, I am trying to unit test this last piece of code that I have attached. What would be the best way to test that my code actually added the object into the database without actually adding it to the database.

CodePudding user response:

Before writing unit tests for the method Insert, you should ask yourself what exactly you wish to "unit test". Insert returns nothing (void), so no return value to test against. However, it throws an exception when some conditions are fulfilled, and it also has a side effect (calling ExecuteNonQuery to put some data in database).

No matter what scenario you decide to test (be it exception being thrown or side effect occurring), you should tell your testing framework that when it reaches the line where side effect takes place, it should replace it with another operation that actually does nothing (not only so that actual database won't get "dirty", but also because external dependencies might consume time or even worse, make your test fail because they fail themselves, and basically we don't care for such dependencies in the context of the object we test).

All standard testing frameworks/libraries provide tools for making assertions about exceptions and side effects, but please note that mocking your side effect (and generally mocking dependencies) is essential not only when you test against a return value or an exception, but also when you wish to verify that side effect itself occurs. Because either way we want to prevent external dependencies from interfering with out test.

Regardless of what testing framework you use, working with interfaces in your product code is generally preferred in order to facilitate flexibility and testability of your application, and it will also let you mock "side effect" methods such as ExecuteNonQuery (that is ISqlCommand rather than SqlCommand). This is because when you instruct to mock an interface method, the framework creates a new dummy class implementing that same interface.

To conclude, testing that object is "added" into database can be done by:

  1. Changing your code to work with interfaces (ISqlConnection, ISqlCommand).
  2. Mocking desired interfaces' methods (Open, ExecuteNonQuery, Close).
  3. Executing Insert.
  4. Verifying that ExecuteNonQuery has been invoked.

(Whether object has been properly added is a different story, which probably calls for integration tests.)

  • Related