解剖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。因此,我需要測試多種不同的升級途徑確保完全的兼容性。
然而,兼容性測試的優先級在我的測試優先級列表里比較低,因為這些兼容性測試會花費很多時間
第十一篇完
文章列表