解剖SQLSERVER 第十一篇 对SQLSERVER的多个版本进行自动化测试(译)

原文:解剖SQLSERVER 第十一篇 对SQLSERVER的多个版本进行自动化测试(译)

解剖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。因此,我需要测试多种不同的升级途径确保完全的兼容性。

然而,兼容性测试的优先级在我的测试优先级列表里比较低,因为这些兼容性测试会花费很多时间

第十一篇完

时间: 2024-11-18 15:32:48

解剖SQLSERVER 第十一篇 对SQLSERVER的多个版本进行自动化测试(译)的相关文章

解剖SQLSERVER 第八篇 OrcaMDF 现在支持多数据文件的数据库(译)

原文:解剖SQLSERVER 第八篇 OrcaMDF 现在支持多数据文件的数据库(译) 解剖SQLSERVER 第八篇  OrcaMDF 现在支持多数据文件的数据库(译) http://improve.dk/orcamdf-now-supports-databases-with-multiple-data-files/ OrcaMDF 其中一个最新特性是支持多数据文件的数据库.这在解析上面需要作出相关的小改变,实际上大部分都是bug 修复代码 由于之前只支持单个数据文件而引起的.然而这确实需要一

解剖SQLSERVER 第六篇 对OrcaMDF的系统测试里避免regressions(译)

原文:解剖SQLSERVER 第六篇 对OrcaMDF的系统测试里避免regressions(译) 解剖SQLSERVER 第六篇  对OrcaMDF的系统测试里避免regressions (译) http://improve.dk/avoiding-regressions-in-orcamdf-by-system-testing/ 当我继续添加新功能和新的数据结构支持进去OrcaMDF软件的时候,bug的风险不断增加 特别是当我开发一个很大的未知功能时,我不能预估结构和该结构的关联,为了降低风

解剖SQLSERVER 第五篇 OrcaMDF里读取Bits类型数据(译)

原文:解剖SQLSERVER 第五篇 OrcaMDF里读取Bits类型数据(译) 解剖SQLSERVER 第五篇  OrcaMDF里读取Bits类型数据(译) http://improve.dk/reading-bits-in-orcamdf/ Bits类型的存储跟SQLSERVER其他定长数据类型的存储很不一样.通常,所有定长列都会显示出来,一个条记录里定长数据部分的字段数据总是一个挨着一个 我们可以写入磁盘的最小数据单位是一个字节,存储位类型数据的天真的方法就是使用一整个(字节@)来存储每一

解剖SQLSERVER 第七篇 OrcaMDF 特性概述(译)

原文:解剖SQLSERVER 第七篇 OrcaMDF 特性概述(译) 解剖SQLSERVER 第七篇  OrcaMDF 特性概述(译) http://improve.dk/orcamdf-feature-recap/ 时间过得真快,这已经过了大概四个月了自从我最初介绍我的宠物项目OrcaMDF. 自从项目开始到现在,OrcaMDF发生了很多变化,功能更强了,因此我想提供一个概述对目前OrcaMDF的功能的概述以及我对OrcaMDF未来的计划 页面类型 OrcaMDF 当前支持以下页面的数据完整解

解剖SQLSERVER 第十篇 OrcaMDF Studio 发布+ 特性重温(译)

原文:解剖SQLSERVER 第十篇 OrcaMDF Studio 发布+ 特性重温(译) 解剖SQLSERVER 第十篇  OrcaMDF Studio 发布+ 特性重温(译) http://improve.dk/orcamdf-studio-release-feature-recap/ 自从我上次作了一个OrcaMDF特性概述之后,两个半月过去了. 只是两个半月过去了自从我上次一个OrcaMDF特性概述.从那时起我一直在忙着参加SQLSERVER的最顶级的三个会议:SQLBits.SQLPA

解剖SQLSERVER 第十三篇 Integers在行压缩和页压缩里的存储格式揭秘(译)

原文:解剖SQLSERVER 第十三篇 Integers在行压缩和页压缩里的存储格式揭秘(译) 解剖SQLSERVER 第十三篇    Integers在行压缩和页压缩里的存储格式揭秘(译) http://improve.dk/the-anatomy-of-row-amp-page-compressed-integers/ 当解决OrcaMDF对行压缩的支持的时候,视图解析整数的时候遇到了一些挑战. 和正常的未压缩整数存储不同的是这些都是可变长度--这意味着1个整数的值50只占用1个字节,而不是

解剖SQLSERVER 第四篇 OrcaMDF里对dates类型数据的解析(译)

解剖SQLSERVER 第四篇  OrcaMDF里对dates类型数据的解析(译) http://improve.dk/parsing-dates-in-orcamdf/ 在SQLSERVER里面有几种不同的date相关类型,当前OrcaMDF 支持三种最常用的date类型:date,datetime,smalldatetime SqlDate实现 date 类型在三种类型之中是最简单的,他是一个3个字节的定长类型,存储了日期值它支持的日期范围从0001-01-01到9999-12-31 默认值

解剖SQLSERVER 第三篇 数据类型的实现(译)

解剖SQLSERVER 第三篇  数据类型的实现(译) http://improve.dk/implementing-data-types-in-orcamdf/ 实现对SQLSERVER数据类型的解析在OrcaMDF 软件里面是一件比较简单的事,只需要实现ISqlType 接口 public interface ISqlType { bool IsVariableLength { get; } short? FixedLength { get; } object GetValue(byte[]

解剖SQLSERVER 第十七篇 使用 OrcaMDF Corruptor 故意损坏数据库(译)

解剖SQLSERVER 第十七篇 使用 OrcaMDF Corruptor 故意损坏数据库(译) http://improve.dk/corrupting-databases-purpose-using-orcamdf-corruptor/ 有时候你必须先作恶,后行善.情况就是 当你想磨练你的数据库修复技能 我现在添加了一个Corruptor 类到OrcaMDF里面 去测试新的RawDatabase 的功能.Corruptor 就跟他的名字一样--他会故意损坏数据库文件 Corruptor 本身