我正在试图弄清楚如何在我创建的类上运行单元测试来对数据库执行查询,但是我一直在试图弄清楚如何伪造所有依赖项。我的类实现了IDisposable,如果打开则关闭SqlConnection。用于选择数据的典型方法(我有几个重载)如下所示:
public DataSet SelectData(string selectCommand)
{
if (!string.IsNullOrEmpty(selectCommand))
{
DataSet ds = new DataSet();
ds.Locale = CultureInfo.InvariantCulture;
SqlDataAdapter adapter = new SqlDataAdapter(selectCommand, Connection);
adapter.Fill(ds);
return ds;
}
throw new ArgumentException("SelectCommand was null or empty", "selectCommand");
}
请注意,SqlCommand构造函数中的Connection参数是一个属性,它返回此类实例的SqlConnection。显然我需要以某种方式伪造SqlDataAdapter,但这也意味着我必须伪造SqlCommand和SqlConnection使用。所有类都是密封的,所以我不能简单地创建一个从这些类继承的伪对象。创建数据库排序会破坏依赖注入的目的,所以我想避免这种情况。有没有人建议如何测试这种方法?
答案 0 :(得分:8)
作为一般规则,要模拟密封类(1),您需要一个可以执行此操作的模拟框架,或者(2)您需要在密封类周围编写(未密封)包装器并使用/ mock那些。 TypeMock可以模拟密封的类,但它需要花钱。但是,请注意,模拟密封类和其他通常不可模拟的项目的能力可以使您不必将代码重构为更好的设计(假设您同意可测试代码是更好的代码)。包装器或适配器相对容易编写,但它们本身无法测试,原因与您编写它们的原因完全相同。但是,由于它们很简单,你可以经常通过检查来证明它们是正确的。
另外,您可能希望使用对象关系映射器(ORM)来查看更现代的数据访问机制。实体框架,LINQ-to-SQL,nHibernate,Subsonic ......在我看来,所有这些都比在低级别编写自己的数据访问层更好。
答案 1 :(得分:5)
我认为我们有所有的接口(IDbConnection,IDbTransaction,IDbCommand和IDbDataAdapter)来模拟我们使用的所有方法,使用NSubstitute和Dependency Injection。
//using init class to inject IDbDataAdapter
IDbDataAdapter adapter = init.DbAdapter("command");
adapter.Connection = connection;
对于SqlConnection和SqlCommand
using (IDbConnection connection = init.DbConnection("connection"))
{
using (IDbTransaction transaction = connection.BeginTransaction())
{
using (IDbCommand command = init.DbCommand("sproc"))
{
command.Transaction = transaction;
command.Connection = connection;
...
}
}
}
单元测试看起来像这样
//arrange
var connection = Substitute.For<IDbConnection>();
var command = Substitute.For<IDbCommand>();
var transaction = Substitute.For<IDbTransaction>();
//this is the init class used before
var init = Substitute.For<ISqlInitializer>();
connection.Open();
connection.BeginTransaction(Arg.Any<IsolationLevel>()).Returns(transaction);
init.DbConnection(Arg.Any<string>()).Returns(connection);
init.DbCommand(Arg.Any<string>()).Returns(command);
var client = new SqlClient(init);
//act
var result = await client.CommandMultipleAsync(new SqlConfiguration(FakeConnection, new List<string> { "testSproc1", "testSproc2" }));
//assert
Assert.AreEqual(0, result);
command.Received(2).ExecuteNonQuery();
transaction.Received(1).Commit();
请查看GitHub上的概念验证项目以获取详细用法,欢迎提出任何建议或反馈:p https://github.com/TianyuanC/dals/blob/master/DALs.Sql/SqlClient.cs