单元测试提供了对一个系统的单个组成部分进行结构化和自动化的测试的方法。尤其数据库单元测试是测试应用程序不同组成部分间所使用的数据。随着数据质量对于任何公司来讲都越来越重要,数据库单元测试也变成软件质量保证的一个更加重要的部分。通过开发数据库单元测试,你可以创建一组测试,并在开发过程中运行它们来确保你的功能按照你所期望的运作。这样的一组测试对于回归测试是非常有用的。当一个应用程序升级了或重新开发,有了单元测试用例,你可以验证这个应用程序的数据输出在不同版本间是否是一致的。本篇文章中,将介绍怎样实现数据库单元测试来比较使用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类做这个工作。