作者: 代羽, 出處:IT專家網, 責任編輯: 李春禹, 2010-04-19 10:54 單元測試提供了對一個系統的單個組成部分進行結構化和自動化的測試的方法。尤其數據庫單元測試是測試應用程序不同組成部分間所使用的數據。隨着數據質量對於任何公司來講都越來越重要,數據庫單元測試也變成軟件質量保證的一個更加重要的部分。 單元測試提供了對一個系統的單個組成部分進行結構化和自動化的測試的方法。尤其數據庫單元測試是測試應用程序不同組成部分間所使用的數據。隨着數據質量對於任何公司來講都越來越重要,數據庫單元測試也變成軟件質量保證的一個更加重要的部分。通過開發數據庫單元測試,你可以創建一組測試,並在開發過程中運行它們來確保你的功能按照你所期望的運作。這樣的一組測試對於迴歸測試是非常有用的。當一個應用程序升級了或重新開發,有了單元測試用例,你就可以驗證這個應用程序的數據輸出在不同版本間是否是一致的。本篇文章中,將介紹怎樣實現數據庫單元測試來比較使用C#和 LINQ生成的兩個結果集。我們將在開源單元測試框架NUnit上建立我們的測試用例,因爲它是完全用C#編寫的,非常快速而且易於使用。在使用NUnit之前,你需要到http://www.nunit.org/index.php?p=download下載這個框架並將它安裝在你的機器上。安裝指導在http://www.nunit.org/index.php?p=installation&r=2.5.2。你可以按照本文中的方法來開發你自己的單元測試用例。如果你對編程不熟悉,那麼這裏有一些可用的數據庫單元測試軟件。http://www.anydbtest.com的AnyDBTest是一個很好的產品。它是第一個也是唯一一個使用XML作爲測試用例的自動化的可用數據庫單元測試工具。它還支持異構數據源間的數據校驗,包括Oracle、SQL Server和MySQL等等。 假設你是一名工作於一家傢俱商店的數據庫管理員。你公司的銷售來源於傢俱銷售和服務銷售,例如,安裝。PnL 報表的現有版本只展示總銷售數目。你公司的會計部門可能希望在這個PnL報表上有傢俱和服務的詳細銷售。開發人員開發了一個新的PnL報表應用程序版本。在發佈新的PnL報表之前,你和開發人員一起使用現有的和新的PnL報表應用程序進行單元測試數據庫表中所保存的數據,來確保數字是一致的。下面是現有的和新的PnL表的schema。 Use testDB GO CREATE TABLE OldPnLReport ( AccountNumber int, NetSales decimal(9,2), Costs decimal(9,2), NetIncome decimal(9,2), ReportDate smalldatetime ) GO CREATE TABLE NewPnLReport ( AccountNumber int, FurnitureSales decimal(9,2), ServiceSales decimal(9,2), Costs decimal(9,2), NetIncome decimal(9,2), ReportDate smalldatetime ) GO 你想將從NewPnLReport中FurnitureSales和ServiceSales得到的合計NetSales與OldPnLReport做比較。下面是這些查詢。 SELECT [AccountNumber] ,[NetSales] ,[Costs] ,[NetIncome] FROM [testDB].[dbo].[OldPnLReport] WHERE ReportDate='2009-10-16' GO SELECT [AccountNumber] ,([FurnitureSales] + [ServiceSales]) as [NetSales] ,[Costs] ,[NetIncome] FROM [testDB].[dbo].[NewPnLReport] WHERE ReportDate='2009-10-16' GO 上面是要比較的結果集。在我們的測試應用程序中,我們首先從數據庫獲得我們的結果集,然後將它們轉化成內存DataTable對象,從而爲比較做準備。TestDataTable類的構造函數接受一個數據庫連接字符串和一個select查詢。你還可以使用SetInputParameter函數設置輸入參數。這個類的Table屬性返回一個DataTable,它包含這個select查詢的結果集。 class TestDataTable { private readonly SqlCommand _command; public TestDataTable(String connstr, String selectQuery, CommandType commandType) { SqlConnection conn = new SqlConnection(connstr); _command = new SqlCommand(selectQuery, conn); _command.CommandType = commandType; } public void SetInputParameter(string parameterName, object parameterValue) { if (_command.Parameters.Contains(parameterName)) _command.Parameters[parameterName] = new SqlParameter(parameterName, parameterValue); else _command.Parameters.AddWithValue(parameterName, parameterValue); } public DataTable Table { get { DataTable _dataTable = new DataTable(); SqlDataAdapter da = new SqlDataAdapter(_command); da.Fill(_dataTable); da.Dispose(); return _dataTable; } } } 我們還需要一個類使用LINQ來比較兩個DataTable對象,並將兩個結果集間的差異寫入在一個變量logFilePath中指定的文件中。ResultSetComparer類做這個工作。
單元測試提供了對一個系統的單個組成部分進行結構化和自動化的測試的方法。尤其數據庫單元測試是測試應用程序不同組成部分間所使用的數據。隨着數據質量對於任何公司來講都越來越重要,數據庫單元測試也變成軟件質量保證的一個更加重要的部分。
class ResultSetComparer { private static StreamWriter sw = null; private static void InitWriter(String filePath) { sw = new StreamWriter(filePath, true); } private static void Write(DataTable dt) { int i = 0; try { sw.WriteLine(System.DateTime.Now.ToString()); sw.WriteLine(); for (i = 0; i < dt.Columns.Count - 1; i++) { sw.Write(dt.Columns[i].ColumnName + " | "); } sw.Write(dt.Columns[i].ColumnName); sw.WriteLine(); foreach (DataRow row in dt.Rows) { object[] array = row.ItemArray; for (i = 0; i < array.Length - 1; i++) { sw.Write(array[i].ToString() + " | "); } sw.Write(array[i].ToString()); sw.WriteLine(); } sw.Close(); } catch (Exception ex) { Console.WriteLine("Invalid Operation when writing data table: /n" + ex.ToString()); } } public static bool AreIdenticalResultSets(DataTable dt1, DataTable dt2, String logFilePath) { var v1 = dt1.AsEnumerable(); var v2 = dt2.AsEnumerable(); var diff1 = v1.Except(v2, DataRowComparer.Default); var diff2 = v2.Except(v1, DataRowComparer.Default); if (diff1.Any()) { InitWriter(logFilePath); sw.WriteLine("Rows in the first table, but not in the second table:"); DataTable diffTbl1 = diff1.CopyToDataTable(); Write(diffTbl1); sw.Close(); } if (diff2.Any()) { InitWriter(logFilePath); sw.WriteLine("Rows in the second table, but not in the first table:"); DataTable diffTbl2 = diff2.CopyToDataTable(); Write(diffTbl2); sw.Close(); } return !(diff1.Any() || diff2.Any()); } } |
這個屬性標識一個包含測試和可選的建立或刪除方法的類。
SetUp屬性
這個屬性用在TestFixture中,用以提供一組通用的、在每個測試方法調用之前執行的函數。
Test屬性
這個屬性標識在一個TestFixture 類中的一個方法作爲一個測試。
Assertions
assertion是一個函數或宏,它覈實測試下單元的動作(或狀態)。assertion失敗會拋出一個異常,中斷當前測試的執行。
下面是我們的測試內容。
[TestFixture] public class DBTest { private DataTable dt1; private DataTable dt2; private String logFilePath = "C://Windows//Temp//PnLDiff.txt"; [SetUp] public void Init() { String comparedDate = "2009-10-16"; String connstr1 = "Data Source=localhost;Initial Catalog=TestDB;Integrated Security=true"; String selectQry1 = "SELECT [AccountNumber],[NetSales],[Costs],[NetIncome] " + "FROM [testDB].[dbo].[OldPnLReport] " + "WHERE vol.[ModifiedDate] >= @comparedDate;"; TestDataTable tdt1 = new TestDataTable(connstr1, selectQry1, CommandType.Text); tdt1.SetInputParameter("@comparedDate", comparedDate); dt1 = tdt1.Table; String connstr2 = "Data Source=localhost;Initial Catalog=TestDB;Integrated Security=true"; String selectQry2 = "SELECT [AccountNumber],([FurnitureSales] + [ServiceSales]) as [NetSales],[Costs],[NetIncome] " + "FROM [testDB].[dbo].[NewPnLReport] " + "WHERE [date] >= @comparedDate;"; TestDataTable tdt2 = new TestDataTable(connstr2, selectQry2, CommandType.Text); tdt2.SetInputParameter("@comparedDate", comparedDate); dt2 = tdt2.Table; } [Test] public void ResultSetEqual_Test() { Assert.IsTrue(ResultSetComparer.AreIdenticalResultSets(dt1, dt2, logFilePath )); } } 讓我們運行nunit-console命令來測試我們的用例。 cd "C:/Program Files (x86)/NUnit 2.5.2/bin/net-2.0" nunit-console.exe "D:/DatabaseUnitTesting/DBUnitTestDemo/DBUnitTestDemo/bin/Release/DBUnitTestDemo.dll" /nologo /nodots |
圖1
插入兩條記錄到這兩個表中來創建一個失敗的用例。
insert into OldPnLReport
values (1, 5033.00, 2038.00, 2995.00, '2009-10-16');
insert into NewPnLReport
values (1, 2041.00, 2900.00, 2038.00, 2993.00, '2009-10-16');
再次運行上面的命令。
圖2
如同你所看到的,這次測試用例ResultSetEqual_Test失敗了。以記事本打開日誌文件,你將看到這兩個表間的差異。