[英]Unit Test Database Commands in 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 類並在方法“插入”中執行以下操作:
/// <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.");
}
}
同樣,我正在嘗試對我附加的最后一段代碼進行單元測試。 測試我的代碼是否將對象實際添加到數據庫中而不實際將其添加到數據庫中的最佳方法是什么。
在為方法Insert
編寫單元測試之前,您應該問自己究竟希望“單元測試”什么。 Insert
返回任何內容 (void),因此沒有要測試的返回值。 但是,它在滿足某些條件時會拋出異常,並且它也有副作用(調用ExecuteNonQuery
將一些數據放入數據庫中)。
無論您決定測試什么場景(無論是拋出異常還是出現副作用),您都應該告訴您的測試框架,當它到達發生副作用的行時,它應該用另一個實際上什么都不做的操作替換它(不僅如此實際數據庫不會變得“臟”,而且因為外部依賴項可能會消耗時間,甚至更糟,使您的測試失敗,因為它們本身失敗了,並且基本上我們不關心在上下文中的此類依賴項我們測試的對象)。
所有標准測試框架/庫都提供了對異常和副作用進行斷言的工具,但請注意,模擬您的副作用(通常是模擬依賴項)不僅在您針對返回值或異常進行測試時必不可少,而且在您進行測試時也很重要希望驗證副作用本身是否發生。 因為無論哪種方式,我們都希望防止外部依賴項干擾測試。
無論您使用什么測試框架,在您的產品代碼中使用接口通常是首選,以促進您的應用程序的靈活性和可測試性,並且它還可以讓您模擬“副作用”方法,例如ExecuteNonQuery
(即ISqlCommand
而不是SqlCommand
)。 這是因為當您指示模擬接口方法時,框架會創建一個新的虛擬類來實現相同的接口。
總而言之,可以通過以下方式測試對象是否“添加”到數據庫中:
Open
、 ExecuteNonQuery
、 Close
)。Insert
。ExecuteNonQuery
是否已被調用。(是否正確添加了對象是另一回事,這可能需要集成測試。)
問題中提供的這段代碼具有三個職責:
SqlCommand
並填充參數;SqlCommand
最終會有一些副作用; 顯然,這些職責中的每一個都需要進行測試,問題是對每個職責使用相同的測試方法幾乎沒有意義。 雖然您可以對第 1 點和第 3 點(下面有更多內容)進行單元測試,但嘗試為第 2 點這樣做是無益的。 SqlCommand
執行涉及與外部系統(即數據庫)的交互,即您需要實現某種數據庫組件隔離。 而且你沒有那么多選擇來這樣做,而每一個都遠非理想。
選項是(至少我知道的那些):
ISqlCommand
)並模擬它們(使用諸如NSubstitute 之類的東西),以便您在嘗試訪問真實數據庫時不做任何事情。 您只需要在模擬上斷言,以查看是否以正確的順序使用預期的參數調用了正確的方法。 這種方法在幾個方面是不好的:
Sqlite
或EF Core
。
解決該問題的另一種方法是針對真實數據庫(不是生產數據庫,而是專門為測試准備的)編寫單元測試,而是編寫集成測試。 您只需要在測試執行之前/之后清理它。 我個人認為這種方法最適合用於測試數據庫交互,因為您正在最接近生產環境的環境中測試應用程序行為。 缺點是測試設置有點復雜,測試運行很可能需要更多時間。 像Reseed (我正在編寫它)或Respawn 之類的庫可能有助於簡化設置並優化執行速度。
現在回到職責 1 和 3 的單元測試。應該可以通過稍微更改您的方法設計來為它們編寫單元測試。 您可能會提取一些額外的方法,每個方法都將顯式處理每個職責。 所以最初的Insert
現在可以看作是三個方法的組合:
SqlCommand
參數的CreateParameters
– 您將能夠斷言純單元測試正確填充了參數;Execute
以使用真實數據庫執行命令——這可以在集成測試中進行測試;AssertUserCreated
會拋出異常——同樣,純單元測試就足夠了。這將使您能夠達到 100% 的測試覆蓋率,而這並不是必需的。 如果參數創建邏輯和存儲過程結果驗證邏輯像現在這樣簡單,則第 1 點和第 3 點可以不進行測試。
當然,設計還可以進一步改進,使其不僅更具可測試性,而且更具可讀性,這只是一個想法。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.