单元测试难题:OleDbCommand
Unit testing conundrum: OleDbCommand
我尝试进行单元测试的 类 之一使用了 OleDbConnection 对象。该对象负责根据传递的 SQL 字符串返回保存在 MS Access 文件中的信息。代码是:
public void getInformation(DateTime start, DateTime finish)
{
string getInfo = "";
string query = "//Query for MS Access goes here";
transactions = addTransactions(query, conn);
}
其中 addTransactions 是一个私有方法,returns 从数据库中检索的列表:
private List<string> addTransactions(string query, OleDbConnection conn)
{
List<string> returnedData = new List<string>();
OleDbCommand cmd = new OleDbCommand(query, conn);
try
{
if (conn.State == ConnectionState.Closed) conn.Open();
OleDbDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
if (!reader.IsDBNull(0))
}
returnedData.Add(reader.GetString(0));
}
catch (Exception ex)
{
Console.WriteLine("OLEB: {0}", ex.Message);
}
conn.Close();
return returnedData;
}
现在,通过查看此方法我已经知道很难进行单元测试,因为 public getInformation() 方法无效。我能做的是测试返回的数据中是否包含某些内容。我的问题是如何编写涉及 OleDbConnection 的单元测试?
我想到的唯一 2 个可能的解决方案涉及创建一个临时访问文件(不理想,因为我们现在不在单元测试的范围内,而且当测试 运行 时可能会失败在其他开发人员的机器上)或为 OleDbConnection 创建一个包装器并在单元测试中使用它(这听起来是最好的方法,但我不知道从哪里开始)。
只需创建一个 MS 文件并将其放入包中,然后始终使用相同的文件进行测试,这样您甚至可以写入数据并确切知道它返回的内容,而不是假设返回的任何数据都是正确的!只是一个想法,有多种实现方式。
or creating a wrapper for OleDbConnection and using that in the unit test
这就是我的方法。不直接 mockable/testable 的对象通常代表其自身的依赖关系。并且应该提供所有依赖项而不是在内部实例化。
包装器可以是提供相同功能的简单传递。让它由一个接口和该接口的代码驱动,这样您就可以为该接口提供模拟以进行单元测试。 (作为额外的好处,即使在测试方法 returns void
时,你的模拟也会给你一些观察的东西。所以你不必深入研究你正在测试的系统,只需观察模拟中调用的内容即可。)
从您正在模拟的聚合根开始,您需要包装 OleDbConnection
。一个简单的入门示例可能如下所示:
public interface IDbConnection
{
IDbCommand CreateCommand();
// other methods
}
public class MyOleDbConnection : IDbConnection
{
private OleDbConnection conn;
public MyOleDbConnection()
{
// initialize "conn" here
}
public IDbCommand CreateCommand()
{
return conn.CreateCommand();
}
// other pass-thru methods
}
本质上,它只是底层对象公开的相同功能的直通包装器。这样做的好处是 this 对象实际上 不需要 进行单元测试,因为它不包含任何有意义的可测试逻辑。 (当然,如果能达到 100% 就好了,但是 return 的投资就没有这个 class。)
如您所见,这还涉及以类似方式为 OleDbCommand
创建包装器。同样,只是传递方法。
一旦您的依赖项被包装并可模拟,您将更改您的 class 以使用包装器。例如,而不是这个:
OleDbCommand cmd = new OleDbCommand(query, conn);
它在内部包含对依赖项的引用,您可以这样做:
IDbCommand cmd = conn.CreateCommand();
它要求依赖项提供对某物的引用。这将维护依赖关系的责任放在了依赖关系本身上,而不是在您要测试的 class 中。 (并且,在这样做的过程中,试图保持对依赖的自由。)
一旦从您的 class 中完全删除依赖项,您就可以自由地模拟和测试您喜欢的一切。
我尝试进行单元测试的 类 之一使用了 OleDbConnection 对象。该对象负责根据传递的 SQL 字符串返回保存在 MS Access 文件中的信息。代码是:
public void getInformation(DateTime start, DateTime finish)
{
string getInfo = "";
string query = "//Query for MS Access goes here";
transactions = addTransactions(query, conn);
}
其中 addTransactions 是一个私有方法,returns 从数据库中检索的列表:
private List<string> addTransactions(string query, OleDbConnection conn)
{
List<string> returnedData = new List<string>();
OleDbCommand cmd = new OleDbCommand(query, conn);
try
{
if (conn.State == ConnectionState.Closed) conn.Open();
OleDbDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
if (!reader.IsDBNull(0))
}
returnedData.Add(reader.GetString(0));
}
catch (Exception ex)
{
Console.WriteLine("OLEB: {0}", ex.Message);
}
conn.Close();
return returnedData;
}
现在,通过查看此方法我已经知道很难进行单元测试,因为 public getInformation() 方法无效。我能做的是测试返回的数据中是否包含某些内容。我的问题是如何编写涉及 OleDbConnection 的单元测试?
我想到的唯一 2 个可能的解决方案涉及创建一个临时访问文件(不理想,因为我们现在不在单元测试的范围内,而且当测试 运行 时可能会失败在其他开发人员的机器上)或为 OleDbConnection 创建一个包装器并在单元测试中使用它(这听起来是最好的方法,但我不知道从哪里开始)。
只需创建一个 MS 文件并将其放入包中,然后始终使用相同的文件进行测试,这样您甚至可以写入数据并确切知道它返回的内容,而不是假设返回的任何数据都是正确的!只是一个想法,有多种实现方式。
or creating a wrapper for OleDbConnection and using that in the unit test
这就是我的方法。不直接 mockable/testable 的对象通常代表其自身的依赖关系。并且应该提供所有依赖项而不是在内部实例化。
包装器可以是提供相同功能的简单传递。让它由一个接口和该接口的代码驱动,这样您就可以为该接口提供模拟以进行单元测试。 (作为额外的好处,即使在测试方法 returns void
时,你的模拟也会给你一些观察的东西。所以你不必深入研究你正在测试的系统,只需观察模拟中调用的内容即可。)
从您正在模拟的聚合根开始,您需要包装 OleDbConnection
。一个简单的入门示例可能如下所示:
public interface IDbConnection
{
IDbCommand CreateCommand();
// other methods
}
public class MyOleDbConnection : IDbConnection
{
private OleDbConnection conn;
public MyOleDbConnection()
{
// initialize "conn" here
}
public IDbCommand CreateCommand()
{
return conn.CreateCommand();
}
// other pass-thru methods
}
本质上,它只是底层对象公开的相同功能的直通包装器。这样做的好处是 this 对象实际上 不需要 进行单元测试,因为它不包含任何有意义的可测试逻辑。 (当然,如果能达到 100% 就好了,但是 return 的投资就没有这个 class。)
如您所见,这还涉及以类似方式为 OleDbCommand
创建包装器。同样,只是传递方法。
一旦您的依赖项被包装并可模拟,您将更改您的 class 以使用包装器。例如,而不是这个:
OleDbCommand cmd = new OleDbCommand(query, conn);
它在内部包含对依赖项的引用,您可以这样做:
IDbCommand cmd = conn.CreateCommand();
它要求依赖项提供对某物的引用。这将维护依赖关系的责任放在了依赖关系本身上,而不是在您要测试的 class 中。 (并且,在这样做的过程中,试图保持对依赖的自由。)
一旦从您的 class 中完全删除依赖项,您就可以自由地模拟和测试您喜欢的一切。