標籤:style blog http color io os 使用 ar for
解剖SQLSERVER 第十一篇 對SQLSERVER的多個版本進行自動化測試(譯)
http://improve.dk/automated-testing-of-orcamdf-against-multiple-sql-server-versions/
自從我發布了OrcaMDF Studio,我已經意識到SQL2005和SQL2008之間的一些系統資料表的差異。
這些差異導致OrcaMDF 解析失敗因為代碼是針對 2008 R2的格式的
當需要做SQL2005的相容時,我漸漸意識到我需要擴大多個SQLSERVER版本的測試覆蓋,替代之前的只對一個版本的測試。
而且,我也需要對一些特定版本功能進行特定的測試(例如:稀疏列測試只能運行在SQLSERVER2008及以上版本)
NUnit TestCaseSourceAttribute 解決問題
NUnit支援通過TestCase屬性進行內聯參數測試。更好的是,對於動態測試案例我們還可以提供參數資料,使用TestCaseSource屬性,使用TestCaseSource attribute。
首先,我實現了一個簡單的枚舉來覆蓋我目前工作所支援的版本:
public enum DatabaseVersion{ SqlServer2005, SqlServer2008, SqlServer2008R2,}
然後我建立SqlServerTestAttribute類,直接繼承自TestCaseSourceAttribute,就像這樣:
public class SqlServerTestAttribute : TestCaseSourceAttribute{ private static IEnumerable<TestCaseData> versions { get { foreach (var value in Enum.GetValues(typeof(DatabaseVersion))) yield return new TestCaseData(value).SetCategory(value.ToString()); } } public SqlServerTestAttribute() : base(typeof(SqlServerTestAttribute), "versions") { }}
這個SqlServerTestAttribute類告訴TestCaseSourceAttribute 在私人靜態版本屬性(private static)裡去找測試案例的來源資料。
版本屬性裡枚舉出所有的DatabaseVersion值並一個接一個的返回它們——確保將測試類別設定到DatabaseVersion值的名稱
接下來,我將當前的測試轉換為使用新的SqlServerTest attribute,替代先前的vanilla NUnit Test attribute:
[SqlServerTest]public void HeapForwardedRecord(DatabaseVersion version){ ...}
這將導致我所有的測試都需要根據DatabaseVersion枚舉裡面的每個枚舉值都運行一次,自動擷取輸入的版本參數裡面的每一個值
支援不同的開發環境
現在,我不想強迫每個人都安裝所有版本的SQL Server--他們可能只是想軟體支援SQL Server 2005 & 2008R2就夠了。在OrcaMDF.Core.Tests項目裡,我定義了每種受支援的測試資料庫的連接字串,就像這樣
<connectionStrings> <clear/> <add name="SqlServer2005" connectionString="Data Source=.SQL2005;Integrated Security=SSPI"/> <add name="SqlServer2008R2" connectionString="Data Source=.;Integrated Security=SSPI"/></connectionStrings>
如果一個資料庫沒有相應的連接字串(名稱對應的DatabaseVersion 枚舉值)那麼該版本的測試就不會運行。因為這個,我目前忽略SQL Server 2008 並且在我的機器上只安裝了SQL 2005 和SQL 2008R2
為了對當前可用資料庫進行過濾,我修改了我的測試案例讓基類去運行實際的測試,使用lambda運算式:
[SqlServerTest]public void HeapForwardedRecord(DatabaseVersion version){ RunDatabaseTest(version, db => { var scanner = new DataScanner(db); var rows = scanner.ScanTable("HeapForwardedRecord").ToList(); Assert.AreEqual(25, rows[0].Field<int>("A")); Assert.AreEqual("".PadLeft(5000, ‘A‘), rows[0].Field<string>("B")); Assert.AreEqual(28, rows[1].Field<int>("A")); Assert.AreEqual("".PadLeft(4000, ‘B‘), rows[1].Field<string>("B")); });}
這個RunDatabase 方法在SqlServerSystemTestBase類裡面聲明
protected void RunDatabaseTest(DatabaseVersion version, Action<Database> test){ string versionConnectionName = version.ToString(); // Only run test for this version if a connection string has been provided if (ConfigurationManager.ConnectionStrings[versionConnectionName] == null) Assert.Inconclusive(); // Setup database and store file paths, if we haven‘t done so already ensureDatabaseIsSetup(version); // Run actual test using (var db = new Database(databaseFiles[version])) test(db);}
如果對應的連接字串沒有在設定檔裡面聲明,我們會放棄測試並且會將他標記為不確定的- 根據當前的配置情況我們根本不能運行他。
接下來,ensureDatabaseIsSetup()執行通常的配置代碼(詳細內容可以參考先前的文章)
儘管這一次每個資料庫版本,每個測試韌體都需要執行。最後 一個OrcaMDF執行個體將會被建立並傳入實際的測試參數
支援不同SQLSERVER版本的特性
正如前面提到的,我需要針對特定SQLSERVER版本的執行的測試方法。
標準的SqlServerTestAttribute 自動枚舉所有的DatabaseVersion enumeration裡面的值,不過我沒有理由再單獨建立一個SqlServer2005TestAttribute 就像這樣
public class SqlServer2005TestAttribute : TestCaseSourceAttribute{ private static IEnumerable<TestCaseData> versions { get { yield return new TestCaseData(DatabaseVersion.SqlServer2005).SetCategory(DatabaseVersion.SqlServer2005.ToString()); } } public SqlServer2005TestAttribute() : base(typeof(SqlServer2005TestAttribute), "versions") { }}
那如果需要將測試回合在SQL Server 2008上呢?
public class SqlServer2008PlusTestAttribute : TestCaseSourceAttribute{ private static IEnumerable<TestCaseData> versions { get { foreach (var value in Enum.GetValues(typeof(DatabaseVersion))) if((DatabaseVersion)value >= DatabaseVersion.SqlServer2008) yield return new TestCaseData(value).SetCategory(value.ToString()); } } public SqlServer2008PlusTestAttribute() : base(typeof(SqlServer2008PlusTestAttribute), "versions") { }}
一旦我們有attributes,那麼將會比較容易的對於我們的版本運行單獨的測試
[SqlServer2008PlusTest]public void ScanAllNullSparse(DatabaseVersion version){ RunDatabaseTest(version, db => { var scanner = new DataScanner(db); var rows = scanner.ScanTable("ScanAllNullSparse").ToList(); //稀疏列 Assert.AreEqual(null, rows[0].Field<int?>("A")); Assert.AreEqual(null, rows[0].Field<int?>("B")); });}
對於ReSharper test runner 的支援
為了運行測試,我們需要ReSharper 6.0 因為ReSharper 5.1不支援TestCaseSource attribute。
一旦你執行了測試,你將會看到下面的測試結果(已經支援SQL Server 2005 & 2008 R2 測試)
每一個測試案例都會自動對多個版本的DatabaseVersion 進行測試(除瞭解析測試,因為他沒有實現SqlServerSystemTestBase 因此不能運行多版本測試)。
因為我沒有對 SQL Server 2005作出支援所以大部分對 SQL Server 2005的測試都失敗。當我運行測試的時候所有SQL2008的測試都是inconclusive 。
最後,所有對於SQL2008R2的測試都通過了
測試過濾
很明顯,我們不能總是對所有版本的SQLSERVER進行測試,那太浪費時間了。其中一種禁用對特定版本的測試的方法就是刪除連接字串。
然而,這樣依然會產生不明確的輸出,而且總是修改設定檔會很麻煩
不幸的是,ReSharper test runner不支援對使用TestCaseSourceAttribute建立的參數化測試的category 過濾。
我已經在 YouTRACK上面開了一個特性要求,我希望ReSharper團隊會考慮將這個特性添加到ReSharper6.1。如果你也覺得這個特性很棒,請考慮投票支援
幸運的是,NUnit test runner 不支援這種過濾。在NUnit test runner裡開啟OrcaMDF.Core.Tests程式集會給你以下結果
注意在我們運行測試之前,Nunit怎麼知道參數化的測試參數的!同時也需要注意Nunit怎麼讓DifferingRecordFormats 測試只運行在SQL2008或以上 ,
而FGSpecificClusteredAllocation測試只讓運行在SQL2005或以上
幸好,如果我們點擊Categories的tab標籤,我們就可以獲得測試類別的清單
通過明確選取特定的版本類別,我們可以選擇運行某些版本,一旦運行了,沒有被選擇的類別類別頭部的小圓點會變成灰色
可以注意到 已耗用時間用了89秒,基本上1秒一個測試,98%的時間花費在了lob類型特性的測試上。
由於類別格式,我能夠在主要的測試類別上進行選擇,從而輕鬆過濾掉長時間啟動並執行測試專案並只關注快速完成的那些類別。
lob 類型特別需要進行測試因為在資料庫啟動之前他們涉及大量的磁碟活動,配置表和配置行的建立
展望未來
添加新版本就猶如安裝SQLSERVER那樣簡單,在配置項裡添加一個連接字串,最後,添加SQLSERVER版本名字進去DatabaseVersion 枚舉裡。就這麼多了。
進一步,在某種程度上我需要按已排序的測試多種升級途徑。基於我自己做的一些測試,一個從SQL Server 2005升級到SQLSERVER2008 R2之後的資料庫
可能跟在SQLSERVER2008 R2本地建立的資料庫有不同,或者從SQL2008升級到SQL2008R2。因此,我需要測試多種不同的升級途徑確保完全的相容性。
然而,相容性測試的優先順序在我的測試優先順序列表裡比較低,因為這些相容性測試會花費很多時間
第十一篇完
解剖SQLSERVER 第十一篇 對SQLSERVER的多個版本進行自動化測試(譯)