利用COM+对数据库操作进行单元测试
当单元测试需要对数据库执行CRUD(Create,Retrieve,Update,Delete)操作时,测试过后会在我们的数据库中留下大量重复的垃圾数据,这些垃圾很碍眼不是吗?而且我们的下一个测试有可能因为这些垃圾产生一些错误。
那么我们要如何处理这些垃圾数据和保证测试的稳定的呢?显然,我们需要在每次测试之前和测试完成之后让数据库都保持相同的状态。换句话说,就是我们需要"undo"这些在测试中对数据库执行的CRUD操作。
对于我们需要的这种"undo"操作,你以前是怎么做的呢?手动的移除还是使用ADO.NET的事物处理呢?这些方法都可行,但对我们来说还不够好。因为它们都需要我们编写更多的代码,影响我们的开发效率。
现在,就要开始说本文的重点了,利用COM+的自动事务处理功能来帮助我们实现我们的"undo"。
首先,写一个基类,此类能够在每一个方法完成后自动回滚对数据库的操作:
下面再写一个使用此基类的示例程序:
那么我们要如何处理这些垃圾数据和保证测试的稳定的呢?显然,我们需要在每次测试之前和测试完成之后让数据库都保持相同的状态。换句话说,就是我们需要"undo"这些在测试中对数据库执行的CRUD操作。
对于我们需要的这种"undo"操作,你以前是怎么做的呢?手动的移除还是使用ADO.NET的事物处理呢?这些方法都可行,但对我们来说还不够好。因为它们都需要我们编写更多的代码,影响我们的开发效率。
现在,就要开始说本文的重点了,利用COM+的自动事务处理功能来帮助我们实现我们的"undo"。
首先,写一个基类,此类能够在每一个方法完成后自动回滚对数据库的操作:
using System;
using NUnit.Framework;
using System.EnterpriseServices;
namespace TransactionTest
{
[TestFixture]
[Transaction(TransactionOption.Required)]
public class DatabaseFixture:ServicedComponent
{
[TearDown]
public void TransactionTearDown()
{
if(ContextUtil.IsInTransaction)
{
ContextUtil.SetAbort();
}
}
}
}
using NUnit.Framework;
using System.EnterpriseServices;
namespace TransactionTest
{
[TestFixture]
[Transaction(TransactionOption.Required)]
public class DatabaseFixture:ServicedComponent
{
[TearDown]
public void TransactionTearDown()
{
if(ContextUtil.IsInTransaction)
{
ContextUtil.SetAbort();
}
}
}
}
下面再写一个使用此基类的示例程序:
using System;
using NUnit.Framework;
using System.Data;
using System.Data.SqlClient;
namespace TransactionTest
{
public class CategoryTests:DatabaseFixture
{
string CONN = @"Server=192.168.0.58\sun;Database=Northwind;uid=帐号;pwd=密码";
[Test]
public void InsertTest()
{
string categoryName = Insert("Test category");
VerifyRowExists(categoryName, true);
}
[Test]
public void DeleteTest()
{
string categoryName = Insert("Test category");
Delete(categoryName);
VerifyRowExists(categoryName, false);
}
/// <summary>
/// 新增一条记录
/// </summary>
/// <param name="categoryName"></param>
/// <returns></returns>
private string Insert(string categoryName)
{
using (SqlConnection conn = new SqlConnection(CONN))
{
string strSQL = "Insert Categories (CategoryName) values('" + categoryName + "')";
SqlCommand cmd = new SqlCommand(strSQL, conn);
conn.Open();
cmd.ExecuteNonQuery();
}
return categoryName;
}
/// <summary>
/// 删除一条记录
/// </summary>
/// <param name="categoryName"></param>
/// <returns></returns>
private string Delete(string categoryName)
{
using (SqlConnection conn = new SqlConnection(CONN))
{
string strSQL = "Delete from Categories Where CategoryName='" + categoryName + "'";
SqlCommand cmd = new SqlCommand(strSQL, conn);
conn.Open();
cmd.ExecuteNonQuery();
}
return categoryName;
}
/// <summary>
/// 对执行的数据库操作进行验证
/// </summary>
/// <param name="categoryName"></param>
/// <param name="shouldExist"></param>
private void VerifyRowExists(string categoryName,bool shouldExist)
{
SqlConnection conn = new SqlConnection(CONN);
conn.Open();
string strSQL = "Select * from Categories where CategoryName='" + categoryName + "'";
SqlCommand cmd = new SqlCommand(strSQL, conn);
SqlDataReader dr = cmd.ExecuteReader(CommandBehavior.CloseConnection);
Assert.AreEqual(shouldExist,dr.HasRows);
dr.Close();
}
}
}
using NUnit.Framework;
using System.Data;
using System.Data.SqlClient;
namespace TransactionTest
{
public class CategoryTests:DatabaseFixture
{
string CONN = @"Server=192.168.0.58\sun;Database=Northwind;uid=帐号;pwd=密码";
[Test]
public void InsertTest()
{
string categoryName = Insert("Test category");
VerifyRowExists(categoryName, true);
}
[Test]
public void DeleteTest()
{
string categoryName = Insert("Test category");
Delete(categoryName);
VerifyRowExists(categoryName, false);
}
/// <summary>
/// 新增一条记录
/// </summary>
/// <param name="categoryName"></param>
/// <returns></returns>
private string Insert(string categoryName)
{
using (SqlConnection conn = new SqlConnection(CONN))
{
string strSQL = "Insert Categories (CategoryName) values('" + categoryName + "')";
SqlCommand cmd = new SqlCommand(strSQL, conn);
conn.Open();
cmd.ExecuteNonQuery();
}
return categoryName;
}
/// <summary>
/// 删除一条记录
/// </summary>
/// <param name="categoryName"></param>
/// <returns></returns>
private string Delete(string categoryName)
{
using (SqlConnection conn = new SqlConnection(CONN))
{
string strSQL = "Delete from Categories Where CategoryName='" + categoryName + "'";
SqlCommand cmd = new SqlCommand(strSQL, conn);
conn.Open();
cmd.ExecuteNonQuery();
}
return categoryName;
}
/// <summary>
/// 对执行的数据库操作进行验证
/// </summary>
/// <param name="categoryName"></param>
/// <param name="shouldExist"></param>
private void VerifyRowExists(string categoryName,bool shouldExist)
{
SqlConnection conn = new SqlConnection(CONN);
conn.Open();
string strSQL = "Select * from Categories where CategoryName='" + categoryName + "'";
SqlCommand cmd = new SqlCommand(strSQL, conn);
SqlDataReader dr = cmd.ExecuteReader(CommandBehavior.CloseConnection);
Assert.AreEqual(shouldExist,dr.HasRows);
dr.Close();
}
}
}
如果编译上面的程序,还需要给程序加一个强名称,否则是无法通过的
在命令行使用 sn -k test.snk
然后把 test.snk 拷贝到程序目录中,再设置
[assembly: AssemblyKeyFile(@"..\..\..\test.snk")]
原文:http://weblogs.asp.net/rosherove/articles/dbunittesting.aspx